Browse Source

Pci: Add Pci controller driver and NVMe support

Linux kernel can be booted from PCIe NVMe SSD

Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
Minda Chen 11 months ago
parent
commit
fb9943ffbb

+ 18 - 5
Platform/StarFive/JH7110SeriesPkg/JH7110Board/JH7110.dsc

@@ -153,7 +153,9 @@
   #MachineModeTimerLib|Silicon/RISC-V/ProcessorPkg/Library/RiscVReadMachineModeTimer/MachineModeTimerLib/MachineModeTimerLib.inf
   MachineModeTimerLib|Silicon/RISC-V/ProcessorPkg/Library/RiscVReadMachineModeTimer/EmulatedMachineModeTimerLib/EmulatedMachineModeTimerLib.inf
   CpuExceptionHandlerLib|Silicon/RISC-V/ProcessorPkg/Library/RiscVExceptionLib/CpuExceptionHandlerDxeLib.inf
-
+  #Pci dependencies 
+  PciHostBridgeLib|Silicon/StarFive/JH7110/Library/PciHostBridgeLib/PciHostBridgeLib.inf
+  PciSegmentLib|Silicon/StarFive/JH7110/Library/PciSegmentLib/PciSegmentLib.inf
 
   # Flattened Device Tree (FDT) access library
   FdtLib|EmbeddedPkg/Library/FdtLib/FdtLib.inf
@@ -454,10 +456,6 @@
 !endif
 
   UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.inf
-  MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf {
-    <LibraryClasses>
-      PcdLib|MdePkg/Library/DxePcdLib/DxePcdLib.inf
-  }
   MdeModulePkg/Universal/Metronome/Metronome.inf
   MdeModulePkg/Universal/BdsDxe/BdsDxe.inf
   MdeModulePkg/Universal/ResetSystemRuntimeDxe/ResetSystemRuntimeDxe.inf {
@@ -539,6 +537,21 @@
       GCC:*_*_*_CC_FLAGS       = -DCONFIG_DWSDMMC
   }
 
+   #
+   # PCIe support
+   #
+   MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf {
+    <LibraryClasses>
+      PcdLib|MdePkg/Library/DxePcdLib/DxePcdLib.inf
+  }
+  MdeModulePkg/Bus/Pci/PciHostBridgeDxe/PciHostBridgeDxe.inf
+  #Silicon/StarFive/JH7110/Library/PciHostBridgeLib/PciHostBridgeLib.inf
+  #Silicon/StarFive/JH7110/Library/PciSegmentLib/PciSegmentLib.inf
+  #
+  # NVMe boot devices
+  #
+  MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDxe.inf
+
   #
   # Usb Support
   #

+ 9 - 1
Platform/StarFive/JH7110SeriesPkg/JH7110Board/JH7110.fdf

@@ -142,7 +142,6 @@ INF  MdeModulePkg/Universal/PCD/Dxe/Pcd.inf
 INF  MdeModulePkg/Core/RuntimeDxe/RuntimeDxe.inf
 INF  MdeModulePkg/Universal/SecurityStubDxe/SecurityStubDxe.inf
 INF  UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.inf
-INF  MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf
 INF  MdeModulePkg/Universal/Metronome/Metronome.inf
 INF  EmbeddedPkg/RealTimeClockRuntimeDxe/RealTimeClockRuntimeDxe.inf
 
@@ -211,6 +210,15 @@ INF  EmbeddedPkg/Universal/MmcDxe/MmcDxe.inf
 INF  Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwEmmcDxe.inf
 INF  Silicon/Synopsys/DesignWare/Drivers/DwEmmcDxe/DwSdmmcDxe.inf
 
+#
+# PCIe support
+#
+INF  MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf
+INF  MdeModulePkg/Bus/Pci/PciHostBridgeDxe/PciHostBridgeDxe.inf
+
+# NVMe boot devices
+INF MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDxe.inf
+
 #
 # Usb Support
 #

+ 1 - 0
Platform/StarFive/JH7110SeriesPkg/Library/PeiCoreInfoHobLib/PeiCoreInfoHobLib.inf

@@ -29,6 +29,7 @@
   MdeModulePkg/MdeModulePkg.dec
   Platform/StarFive/JH7110SeriesPkg/JH7110SeriesPkg.dec
   Silicon/SiFive/SiFive.dec
+  Silicon/StarFive/JH7110/JH7110.dec
   Platform/RISC-V/PlatformPkg/RiscVPlatformPkg.dec
   Silicon/RISC-V/ProcessorPkg/RiscVProcessorPkg.dec
 

+ 24 - 0
Silicon/StarFive/JH7110/Include/IndustryStandard/JH7110.h

@@ -0,0 +1,24 @@
+/** @file
+ *
+ *  Copyright (c) 2023, Minda Chen <minda.chen@starfivetech.com>.
+ *
+ *  SPDX-License-Identifier: BSD-2-Clause-Patent
+ *
+ **/
+
+#ifndef JH7110_H__
+#define JH7110_H__
+
+//#define JH7110_SOC_REGISTERS               (FixedPcdGet64 (PcdJH7110RegistersAddress))
+//#define JH7110_SOC_REGISTER_LENGTH         0x02000000
+
+/* Generic PCI addresses */
+#define PCIE_TOP_OF_MEM_WIN                                 (FixedPcdGet64 (PcdJH7110PciBusMmioAdr))
+#define PCIE_CPU_MMIO_WINDOW                                (FixedPcdGet64 (PcdJH7110PciCpuMmioAdr))
+#define PCIE_BRIDGE_MMIO_LEN                                (FixedPcdGet32 (PcdJH7110PciBusMmioLen))
+
+/* PCI root bridge control registers location */
+#define PCIE_REG_BASE                                       (FixedPcdGet64 (PcdJH7110PciRegBase))
+#define PCIE_CONFIG_BASE                                    (FixedPcdGet64 (PcdJH7110PciConfigRegBase))
+
+#endif /* JH7110_H__ */

+ 28 - 0
Silicon/StarFive/JH7110/JH7110.dec

@@ -0,0 +1,28 @@
+## @file
+#
+#  Copyright (c) 2023, Minda Chen <minda.chen@starfivetech.com>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  DEC_SPECIFICATION              = 0x0001001A
+  PACKAGE_NAME                   = JH7110Pkg
+  PACKAGE_GUID                   = D4B585C5-EBCA-4779-B974-05A3CF2F10C4
+  PACKAGE_VERSION                = 1.0
+
+[Includes]
+  Include
+
+[Guids]
+  gJH7110TokenSpaceGuid = {0x44045e56, 0x7056, 0x4be6, {0x88, 0xc0, 0x49, 0x0c, 0x67, 0x90, 0x2f, 0xba}}
+
+[PcdsFixedAtBuild.common]
+  gJH7110TokenSpaceGuid.PcdJH7110RegistersAddress|0|UINT32|0x00000001
+  gJH7110TokenSpaceGuid.PcdJH7110PciRegBase|0x2b000000|UINT64|0x00000002
+  gJH7110TokenSpaceGuid.PcdJH7110PciBusMmioAdr|0x0|UINT64|0x00000003
+  gJH7110TokenSpaceGuid.PcdJH7110PciBusMmioLen|0x0|UINT32|0x00000004
+  gJH7110TokenSpaceGuid.PcdJH7110PciCpuMmioAdr|0x0|UINT64|0x00000005
+
+  gJH7110TokenSpaceGuid.PcdJH7110PciConfigRegBase|0x9c0000000|UINT64|0x00000006

+ 192 - 0
Silicon/StarFive/JH7110/Library/PciHostBridgeLib/PciHostBridgeLib.c

@@ -0,0 +1,192 @@
+/** @file
+ *
+ * PCI Host Bridge Library instance for StarFive JH7110 SOC
+ *
+ * Copyright (c) 2023, Minda Chen <minda.chen@starfivetech.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause-Patent
+ *
+ **/
+
+#include <IndustryStandard/JH7110.h>
+#include <IndustryStandard/Pci22.h>
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PcdLib.h>
+#include <Library/PciHostBridgeLib.h>
+#include <PiDxe.h>
+#include <Protocol/PciRootBridgeIo.h>
+#include <Protocol/PciHostBridgeResourceAllocation.h>
+
+#pragma pack(1)
+
+typedef PACKED struct {
+  ACPI_HID_DEVICE_PATH     AcpiDevicePath;
+  EFI_DEVICE_PATH_PROTOCOL EndDevicePath;
+} EFI_PCI_ROOT_BRIDGE_DEVICE_PATH;
+
+#pragma pack ()
+
+STATIC CONST EFI_PCI_ROOT_BRIDGE_DEVICE_PATH mEfiPciRootBridgeDevicePath[] = {
+  {
+    {
+      {
+        ACPI_DEVICE_PATH,
+        ACPI_DP,
+        {
+          (UINT8)(sizeof (ACPI_HID_DEVICE_PATH)),
+          (UINT8)(sizeof (ACPI_HID_DEVICE_PATH) >> 8)
+        }
+      },
+      EISA_PNP_ID (0x0A08), // PCI Express
+      0
+    },
+
+    {
+      END_DEVICE_PATH_TYPE,
+      END_ENTIRE_DEVICE_PATH_SUBTYPE,
+      {
+        END_DEVICE_PATH_LENGTH,
+        0
+      }
+    }
+  },
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED
+CHAR16 *mPciHostBridgeLibAcpiAddressSpaceTypeStr[] = {
+  L"Mem", L"I/O", L"Bus"
+};
+
+// These should come from the PCD...
+#define JH7110_PCI_SEG0_BUSNUM_MIN     0x00
+#define JH7110_PCI_SEG0_BUSNUM_MAX     0xFF
+#define JH7110_PCI_SEG0_PORTIO_MIN     0x01
+#define JH7110_PCI_SEG0_PORTIO_MAX     0x00 // MIN>MAX disables PIO
+#define JH7110_PCI_SEG0_PORTIO_OFFSET  0x00
+// The bridge thinks its MMIO is here (which means it can't access this area in phy ram)
+#define JH7110_PCI_SEG0_MMIO32_MIN     (0x38000000)
+#define JH7110_PCI_SEG0_MMIO32_MAX     (JH7110_PCI_SEG0_MMIO32_MIN + 0x8000000)
+// The CPU views it via a window here..
+//#define BCM2711_PCI_SEG0_MMIO32_XLATE   (PCIE_CPU_MMIO_WINDOW - PCIE_TOP_OF_MEM_WIN)
+
+// We might be able to size another region?
+#define JH7110_PCI_SEG0_MMIO64_MIN     (0x980000000)
+#define JH7110_PCI_SEG0_MMIO64_MAX     (0x9c0000000)
+
+//
+// See description in MdeModulePkg/Include/Library/PciHostBridgeLib.h
+//
+PCI_ROOT_BRIDGE mPciRootBridges[] = {
+  {
+    0,                                      // Segment
+    0,                                      // Supports
+    0,                                      // Attributes
+    FALSE,                                  // DmaAbove4G
+    FALSE,                                  // NoExtendedConfigSpace (true=256 byte config, false=4k)
+    FALSE,                                  // ResourceAssigned
+    EFI_PCI_HOST_BRIDGE_COMBINE_MEM_PMEM |
+    EFI_PCI_HOST_BRIDGE_MEM64_DECODE,   // AllocationAttributes
+    { JH7110_PCI_SEG0_BUSNUM_MIN,
+      JH7110_PCI_SEG0_BUSNUM_MAX },        // Bus
+    { JH7110_PCI_SEG0_PORTIO_MIN,
+      JH7110_PCI_SEG0_PORTIO_MAX,
+      MAX_UINT64 - JH7110_PCI_SEG0_PORTIO_OFFSET + 1 },   // Io
+    { JH7110_PCI_SEG0_MMIO32_MIN,
+      JH7110_PCI_SEG0_MMIO32_MAX, 0},// Mem
+    {JH7110_PCI_SEG0_MMIO64_MIN, JH7110_PCI_SEG0_MMIO64_MAX, 0},     // MemAbove4G            
+    { MAX_UINT64, 0x0 },                    // Pefetchable Mem
+    { MAX_UINT64, 0x0 },                    // Pefetchable MemAbove4G
+    (EFI_DEVICE_PATH_PROTOCOL *)&mEfiPciRootBridgeDevicePath[0]
+  }
+};
+
+/**
+  Return all the root bridge instances in an array.
+
+  @param Count  Return the count of root bridge instances.
+
+  @return All the root bridge instances in an array.
+          The array should be passed into PciHostBridgeFreeRootBridges()
+          when it's not used.
+**/
+PCI_ROOT_BRIDGE *
+EFIAPI
+PciHostBridgeGetRootBridges (
+  OUT UINTN     *Count
+  )
+{
+    *Count = ARRAY_SIZE (mPciRootBridges);
+    return mPciRootBridges;
+}
+
+/**
+  Free the root bridge instances array returned from PciHostBridgeGetRootBridges().
+
+  @param Bridges The root bridge instances array.
+  @param Count   The count of the array.
+**/
+VOID
+EFIAPI
+PciHostBridgeFreeRootBridges (
+  PCI_ROOT_BRIDGE *Bridges,
+  UINTN           Count
+  )
+{
+}
+
+/**
+  Inform the platform that the resource conflict happens.
+
+  @param HostBridgeHandle Handle of the Host Bridge.
+  @param Configuration    Pointer to PCI I/O and PCI memory resource
+                          descriptors. The Configuration contains the resources
+                          for all the root bridges. The resource for each root
+                          bridge is terminated with END descriptor and an
+                          additional END is appended indicating the end of the
+                          entire resources. The resource descriptor field
+                          values follow the description in
+                          EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL
+                          .SubmitResources().
+**/
+VOID
+EFIAPI
+PciHostBridgeResourceConflict (
+  EFI_HANDLE                        HostBridgeHandle,
+  VOID                              *Configuration
+  )
+{
+  EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Descriptor;
+  UINTN                             RootBridgeIndex;
+  DEBUG ((DEBUG_ERROR, "PciHostBridge: Resource conflict happens!\n"));
+
+  RootBridgeIndex = 0;
+  Descriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Configuration;
+  while (Descriptor->Desc == ACPI_ADDRESS_SPACE_DESCRIPTOR) {
+    DEBUG ((DEBUG_ERROR, "RootBridge[%d]:\n", RootBridgeIndex++));
+    for (; Descriptor->Desc == ACPI_ADDRESS_SPACE_DESCRIPTOR; Descriptor++) {
+      ASSERT (Descriptor->ResType <
+              ARRAY_SIZE (mPciHostBridgeLibAcpiAddressSpaceTypeStr));
+      DEBUG ((DEBUG_ERROR, " %s: Length/Alignment = 0x%lx / 0x%lx\n",
+              mPciHostBridgeLibAcpiAddressSpaceTypeStr[Descriptor->ResType],
+              Descriptor->AddrLen, Descriptor->AddrRangeMax
+              ));
+      if (Descriptor->ResType == ACPI_ADDRESS_SPACE_TYPE_MEM) {
+        DEBUG ((DEBUG_ERROR, "     Granularity/SpecificFlag = %ld / %02x%s\n",
+                Descriptor->AddrSpaceGranularity, Descriptor->SpecificFlag,
+                ((Descriptor->SpecificFlag &
+                  EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE_PREFETCHABLE
+                  ) != 0) ? L" (Prefetchable)" : L""
+                ));
+      }
+    }
+    //
+    // Skip the END descriptor for root bridge
+    //
+    ASSERT (Descriptor->Desc == ACPI_END_TAG_DESCRIPTOR);
+    Descriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *)(
+                   (EFI_ACPI_END_TAG_DESCRIPTOR *)Descriptor + 1
+                   );
+  }
+}

+ 48 - 0
Silicon/StarFive/JH7110/Library/PciHostBridgeLib/PciHostBridgeLib.inf

@@ -0,0 +1,48 @@
+## @file
+#
+#  PCI Host Bridge Library instance for StarFive JH7110 SOC
+#  Liberally borrowed from the SynQuacer
+#
+#  Copyright (c) 2023 Minda Chen <minda.chen@starfivetech.com>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010019
+  BASE_NAME                      = PciHostBridgeLib
+  FILE_GUID                      = 8d52c038-0040-11ea-83fb-5254005675a0
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = PciHostBridgeLib|DXE_DRIVER
+  CONSTRUCTOR                    = JH7110PciHostBridgeLibConstructor
+
+#
+# The following information is for reference only and not required by the build
+# tools.
+#
+#  VALID_ARCHITECTURES           = ARM AARCH64 RISCV64
+#
+
+[Sources]
+  PciHostBridgeLib.c
+  PciHostBridgeLibConstructor.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  Silicon/StarFive/JH7110/JH7110.dec
+
+[LibraryClasses]
+  DebugLib
+  DevicePathLib
+  MemoryAllocationLib
+  PcdLib
+  UefiBootServicesTableLib
+
+[FixedPcd]
+  gJH7110TokenSpaceGuid.PcdJH7110PciRegBase
+  gJH7110TokenSpaceGuid.PcdJH7110PciBusMmioAdr
+  gJH7110TokenSpaceGuid.PcdJH7110PciBusMmioLen
+  gJH7110TokenSpaceGuid.PcdJH7110PciCpuMmioAdr

+ 302 - 0
Silicon/StarFive/JH7110/Library/PciHostBridgeLib/PciHostBridgeLibConstructor.c

@@ -0,0 +1,302 @@
+/** @file
+ *
+ * PCI Host Bridge Library instance for StarFive JH7110 SOC
+ *
+ * Copyright (c) 2023, Minda Chen <minda.chen@starfivetech.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause-Patent
+ *
+ * This module initializes the Pci as close to a standard
+ * PCI root complex as possible. The general information
+ * for this driver was sourced from.
+ *
+ *
+ **/
+
+#include <IndustryStandard/JH7110.h>
+#include <IndustryStandard/Pci22.h>
+#include <Library/DebugLib.h>
+#include <Library/IoLib.h>
+#include <Library/PcdLib.h>
+#include <Library/PciHostBridgeLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <PiDxe.h>
+#include <Protocol/PciHostBridgeResourceAllocation.h>
+#include <Library/TimerLib.h>
+
+#define RegWrite(addr,data)            MmioWrite32((addr), (data))
+#define RegRead(addr,data)             ((data) = MmioRead32 (addr))
+
+#define STG_SYSCON_BASE 0x10240000
+
+#define STG_SYSCON_K_RP_NEP_MASK               (1 << 8)
+#define STG_SYSCON_CKREF_SRC_SHIFT             18
+#define STG_SYSCON_CKREF_SRC_MASK              (0x3 << 18)
+#define STG_SYSCON_CLKREQ_MASK			(1 << 22)
+#define STG_SYSCON_BASE 0x10240000
+#define SYS_CLK_BASE 0x13020000
+#define STG_CLK_BASE 0x10230000
+#define SYS_CLK_NOC_OFFSET 0x98
+#define STG_PCIE_CLK_OFFSET 0x20
+#define STG_PCIE_CLKS 0xc
+#define STG_PCIE_RESET_OFFSET 0x74
+#define SYS_GPIO_BASE 0x13040000
+
+#define PREF_MEM_WIN_64_SUPPORT         (1 << 3)
+#define PMSG_LTR_SUPPORT                (1 << 2)
+#define PDLA_LINK_SPEED_GEN2            (1 << 12)
+#define PLDA_FUNCTION_DIS               (1 << 15)
+#define PLDA_FUNC_NUM                   4
+#define PLDA_PHY_FUNC_SHIFT             9
+#define PLDA_RP_ENABLE                  1
+
+#define PCIE_BASIC_STATUS               0x018
+#define PCIE_CFGNUM                     0x140
+#define IMASK_LOCAL                     0x180
+#define ISTATUS_LOCAL                   0x184
+#define IMSI_ADDR                       0x190
+#define ISTATUS_MSI                     0x194
+#define CFG_SPACE                       0x1000
+#define GEN_SETTINGS                    0x80
+#define PCIE_PCI_IDS                    0x9C
+#define PCIE_WINROM                     0xFC
+#define PMSG_SUPPORT_RX                 0x3F0
+#define PCI_MISC                        0xB4
+
+#define STG_SYSCON_AXI4_SLVL_ARFUNC_MASK        0x7FFF00
+#define STG_SYSCON_AXI4_SLVL_ARFUNC_SHIFT       0x8
+#define STG_SYSCON_AXI4_SLVL_AWFUNC_MASK        0x7FFF
+#define STG_SYSCON_AXI4_SLVL_AWFUNC_SHIFT       0x0
+
+
+#define XR3PCI_ATR_AXI4_SLV0            0x800
+#define XR3PCI_ATR_SRC_ADDR_LOW         0x0
+#define XR3PCI_ATR_SRC_ADDR_HIGH        0x4
+#define XR3PCI_ATR_TRSL_ADDR_LOW        0x8
+#define XR3PCI_ATR_TRSL_ADDR_HIGH       0xc
+#define XR3PCI_ATR_TRSL_PARAM           0x10
+#define XR3PCI_ATR_TABLE_OFFSET         0x20
+#define XR3PCI_ATR_MAX_TABLE_NUM        8
+
+#define XR3PCI_ATR_SRC_ADDR_MASK        0xfffff000
+#define XR3PCI_ATR_TRSL_ADDR_MASK       0xfffff000
+#define XR3PCI_ATR_SRC_WIN_SIZE_SHIFT   1
+#define XR3_PCI_ECAM_SIZE		28
+
+#define IDS_PCI_TO_PCI_BRIDGE           0x060400
+#define IDS_CLASS_CODE_SHIFT            8
+#define SYS_GPIO_OUTPUT_OFF		0x40
+
+UINT32 AtrTableNum;
+UINT64 PCIE_CFG_BASE[2] = {0x940000000, 0x9c0000000};
+UINT64 PCI_MEMREGION_32[2] = {0x30000000, 0x38000000};
+UINT64 PCI_MEMREGION_64[2] = {0x900000000, 0x980000000};
+UINT64 PCI_MEMREGION_SIZE[2] = {27, 30};
+UINT32 STG_ARFUNC_OFFSET[2] = {0xc0, 0x270};
+UINT32 STG_AWFUNC_OFFSET[2] = {0xc4, 0x274};
+UINT32 STG_RP_REP_OFFSET[2] = {0x130, 0x2e0};
+UINT32 PCIE_GPIO[2] = {26, 28};
+
+static inline UINT64 get_pcie_reg_base(UINT32 Port)
+{
+    return PCIE_REG_BASE + Port * 0x1000000;
+}
+
+VOID PcieRegWrite(UINT32 Port, UINTN Offset, UINT32 Value)
+{
+    UINT64 base = get_pcie_reg_base(Port);
+
+    RegWrite((UINT64)base + Offset, Value);
+}
+
+UINT32 PcieRegRead(UINT32 Port, UINTN Offset)
+{
+    UINT32 Value = 0;
+    UINT64 base = get_pcie_reg_base(Port);
+
+    RegRead((UINT64)base + Offset, Value);
+    return Value;
+}
+
+static VOID PcieUpdatebits(UINT64 base, UINTN Offset, UINT32 mask, UINT32 val)
+{
+    UINT32 Value = 0;
+
+    Value = MmioRead32((UINT64)base + Offset);
+    Value &= ~mask;
+    Value |= val;
+    MmioWrite32((UINT64)base + Offset, Value);
+}
+
+STATIC
+VOID PcieFuncSet(
+    IN UINT32 Port)
+{
+	int i;
+	UINT32 Value;
+	UINT64 base = get_pcie_reg_base(Port);
+
+	/* Disable physical functions except #0 */
+	for (i = 1; i < PLDA_FUNC_NUM; i++) {
+		PcieUpdatebits(STG_SYSCON_BASE, STG_ARFUNC_OFFSET[Port],
+			STG_SYSCON_AXI4_SLVL_ARFUNC_MASK,
+			(i << PLDA_PHY_FUNC_SHIFT) <<
+			STG_SYSCON_AXI4_SLVL_ARFUNC_SHIFT);
+		PcieUpdatebits(STG_SYSCON_BASE, STG_AWFUNC_OFFSET[Port],
+			STG_SYSCON_AXI4_SLVL_AWFUNC_MASK,
+			(i << PLDA_PHY_FUNC_SHIFT) <<
+			STG_SYSCON_AXI4_SLVL_AWFUNC_SHIFT);
+		PcieUpdatebits(base, PCI_MISC,
+			PLDA_FUNCTION_DIS, PLDA_FUNCTION_DIS);
+	}
+
+	PcieUpdatebits(STG_SYSCON_BASE, STG_ARFUNC_OFFSET[Port],
+			STG_SYSCON_AXI4_SLVL_ARFUNC_MASK, 0);
+	PcieUpdatebits(STG_SYSCON_BASE, STG_AWFUNC_OFFSET[Port],
+			STG_SYSCON_AXI4_SLVL_AWFUNC_MASK, 0);
+
+	/* Enable root port*/
+	PcieUpdatebits(base, GEN_SETTINGS,
+		PLDA_RP_ENABLE, PLDA_RP_ENABLE);
+
+        Value = (IDS_PCI_TO_PCI_BRIDGE << IDS_CLASS_CODE_SHIFT);
+	PcieRegWrite(Port, PCIE_PCI_IDS, Value);
+
+	PcieUpdatebits(base, PMSG_SUPPORT_RX,
+		PMSG_LTR_SUPPORT, 0);
+
+	/* Prefetchable memory window 64-bit addressing support */
+	PcieUpdatebits(base, PCIE_WINROM,
+		PREF_MEM_WIN_64_SUPPORT, PREF_MEM_WIN_64_SUPPORT);
+}
+
+STATIC
+VOID
+PcieSTGInit(
+    IN UINT32 Port)
+{
+
+    PcieUpdatebits(STG_SYSCON_BASE, STG_RP_REP_OFFSET[Port],
+		STG_SYSCON_K_RP_NEP_MASK, 
+		STG_SYSCON_K_RP_NEP_MASK);
+    PcieUpdatebits(STG_SYSCON_BASE, STG_AWFUNC_OFFSET[Port],
+		STG_SYSCON_CKREF_SRC_MASK, 
+		2 << STG_SYSCON_CKREF_SRC_SHIFT);
+    PcieUpdatebits(STG_SYSCON_BASE, STG_AWFUNC_OFFSET[Port],
+		STG_SYSCON_CLKREQ_MASK, 
+		STG_SYSCON_CLKREQ_MASK);
+}
+
+STATIC
+VOID
+PcieClockInit(
+    IN UINT32 Port)
+{
+	RegWrite(STG_CLK_BASE + STG_PCIE_CLK_OFFSET
+		+ Port * STG_PCIE_CLKS, 1 << 31); /*axi mst0*/
+	RegWrite(STG_CLK_BASE + STG_PCIE_CLK_OFFSET
+		+ Port * STG_PCIE_CLKS + 4, 1 << 31); /* apb */
+	RegWrite(STG_CLK_BASE + STG_PCIE_CLK_OFFSET
+		+ Port * STG_PCIE_CLKS + 8, 1 << 31); /* tl0 */
+}
+
+
+STATIC
+VOID
+PcieResetDeassert(
+    IN UINT32 Port)
+{
+	UINT32 Portoffset = Port * 6 + 11;
+
+	PcieUpdatebits(STG_CLK_BASE, STG_PCIE_RESET_OFFSET,
+		0x3f << (Portoffset), 0); /*reset all*/
+}
+
+VOID
+PcieResetAssert(
+    IN UINT32 Port)
+{
+	UINT32 Portoffset = Port * 6 + 11;
+
+	PcieUpdatebits(STG_CLK_BASE, STG_PCIE_RESET_OFFSET,
+		0x3f << (Portoffset), 0x3f << (Portoffset)); /*axi mst0*/
+}
+
+STATIC
+VOID PcieGpioResetSet(
+    IN UINT32 Port,
+    IN UINT32 Value)
+{
+	UINT32 remain, mask;
+
+	remain = PCIE_GPIO[Port] & 0x3;
+	mask = 0xff << (remain * 8);
+	PcieUpdatebits(SYS_GPIO_BASE, SYS_GPIO_OUTPUT_OFF + (PCIE_GPIO[Port] & 0xfffc),
+		mask, Value << (remain * 8));
+}
+
+STATIC
+VOID PcieAtrInit(
+    IN UINT32 Port,
+    IN UINT64 src_addr,
+    IN UINT64 trsl_addr,
+    IN UINT32 win_size,
+    IN UINT32 config)
+{
+	UINT64 base = get_pcie_reg_base(Port) + XR3PCI_ATR_AXI4_SLV0;
+	UINT32 Value;
+
+        base +=  XR3PCI_ATR_TABLE_OFFSET * AtrTableNum;
+	AtrTableNum++;
+
+        /* X3PCI_ATR_SRC_ADDR_LOW:
+         *   - bit 0: enable entry,
+         *   - bits 1-6: ATR window size: total size in bytes: 2^(ATR_WSIZE + 1)
+         *   - bits 7-11: reserved
+         *   - bits 12-31: start of source address
+         */
+	Value = src_addr;
+	//DEBUG ((DEBUG_ERROR, "addr low %x\n", Value));
+	RegWrite(base + XR3PCI_ATR_SRC_ADDR_LOW,
+		(Value & XR3PCI_ATR_SRC_ADDR_MASK) | ((win_size - 1) << 1) | 0x1);
+	Value = src_addr >> 32;
+	//DEBUG ((DEBUG_ERROR, "addr high %x\n", Value));
+	RegWrite(base + XR3PCI_ATR_SRC_ADDR_HIGH, Value);
+	Value = trsl_addr;
+	RegWrite(base + XR3PCI_ATR_TRSL_ADDR_LOW, Value);
+	Value = trsl_addr >> 32;
+	RegWrite(base + XR3PCI_ATR_TRSL_ADDR_HIGH, Value);
+	RegWrite(base + XR3PCI_ATR_TRSL_PARAM, config);
+}
+
+
+EFI_STATUS
+EFIAPI
+JH7110PciHostBridgeLibConstructor (
+  IN EFI_HANDLE       ImageHandle,
+  IN EFI_SYSTEM_TABLE *SystemTable
+  )
+{
+  UINT32		PortIndex = 1;
+
+  DEBUG ((DEBUG_ERROR, "PCIe RootBridge constructor\n"));
+  PcieSTGInit(PortIndex);
+  RegWrite(SYS_CLK_BASE + SYS_CLK_NOC_OFFSET, 1 << 31);
+  PcieClockInit(PortIndex);
+  PcieResetDeassert(PortIndex);
+  PcieGpioResetSet(PortIndex, 0);
+  PcieFuncSet(PortIndex);
+  
+  PcieAtrInit(PortIndex, PCIE_CFG_BASE[PortIndex],
+     0, XR3_PCI_ECAM_SIZE, 1);
+  PcieAtrInit(PortIndex, PCI_MEMREGION_32[PortIndex],
+     PCI_MEMREGION_32[PortIndex], PCI_MEMREGION_SIZE[0], 0);
+  PcieAtrInit(PortIndex, PCI_MEMREGION_64[PortIndex],
+     PCI_MEMREGION_64[PortIndex], PCI_MEMREGION_SIZE[1], 0);
+  PcieGpioResetSet(PortIndex, 1);
+  MicroSecondDelay(300);
+
+  DEBUG ((DEBUG_ERROR, "PCIe port %d init\n", PortIndex));
+
+  return EFI_SUCCESS;
+}

+ 1448 - 0
Silicon/StarFive/JH7110/Library/PciSegmentLib/PciSegmentLib.c

@@ -0,0 +1,1448 @@
+/** @file
+ *
+ * PCI Segment Library for StarFive JH7110 SoC
+ *
+ * Copyright (c) 2023, Minda Chen
+ *
+ * SPDX-License-Identifier: BSD-2-Clause-Patent
+ *
+ **/
+
+#include <Base.h>
+#include <Uefi.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/IoLib.h>
+#include <Library/PcdLib.h>
+#include <Library/PciSegmentLib.h>
+#include <Library/UefiLib.h>
+#include <IndustryStandard/JH7110.h>
+#include <IndustryStandard/Pci30.h>
+
+typedef enum {
+  PciCfgWidthUint8 = 0,
+  PciCfgWidthUint16,
+  PciCfgWidthUint32,
+  PciCfgWidthMax
+} PCI_CFG_WIDTH;
+
+/*
+ * This PCIe config space is unusual...
+ * The root port is the first bytes of the register space (offset 0)
+ * The individual devices are then selected by computing their BDF index
+ * and writing that into the CFG_INDEX register (offset 0x9000)
+ * the "ECAM" data is then read/writeable at CFG_DATA (offset 0x8000)
+ */
+
+#define EFI_PCI_ADDR_BUS(bus) ((bus >> 20) & 0xFF)    /* Note PCI_SEGMENT_LIB_ADDRESS */
+#define EFI_PCI_ADDR_DEV(dev) ((dev >> 15) & 0x1F)
+#define EFI_PCI_ADDR_FUN(fun) ((fun >> 12) & 0x07)
+
+/**
+  Assert the validity of a PCI Segment address.
+  A valid PCI Segment address should not contain 1's in bits 28..31 and 48..63
+
+  @param  A The address to validate.
+  @param  M Additional bits to assert to be zero.
+
+**/
+#define ASSERT_INVALID_PCI_SEGMENT_ADDRESS(A,M) \
+  ASSERT (((A) & (0xffff0000f0000000ULL | (M))) == 0)
+
+/**
+  Given the nature of how we access PCI devices, we ensure that
+  read/write accesses are serialized through the use of a lock.
+**/
+STATIC
+EFI_LOCK mPciSegmentReadWriteLock = EFI_INITIALIZE_LOCK_VARIABLE (TPL_HIGH_LEVEL);
+
+//STATIC UINT64 mPciSegmentLastAccess;     /* Avoid repeat CFG_INDEX updates */
+
+/**
+  Internal worker function to obtain config space base address.
+
+  @param  Address The address that encodes the PCI Bus, Device, Function and
+                  Register.
+
+  @return The value read from the PCI configuration register.
+
+**/
+
+STATIC
+UINT64
+PciSegmentLibGetConfigBase (
+  IN  UINT64                      Address,
+  IN  UINT32			  Write
+  )
+{
+  UINT64        Base;
+  UINT64        Offset;
+  UINT32        Dev;
+  UINT32        Bus;
+
+  Base = PCIE_CONFIG_BASE;
+  Offset = Address & 0xFFF;         /* Pick off the 4k register offset */
+  Address &= 0xFFFF000;            /* Clear the offset leave only the BDF */
+
+  /* The root port is at the base of the PCIe register space */
+  if (Address != 0) {
+      Dev = EFI_PCI_ADDR_DEV (Address);
+      Bus = EFI_PCI_ADDR_BUS (Address);
+      /*
+       * There can only be a single device on bus 1 (downstream of root).
+       * Subsequent busses (behind a PCIe switch) can have more.
+       */
+      if (!Bus && Write && ((Offset == 0x10) || (Offset == 0x14))) {
+         return 0xFFFFFFFF;
+      }
+      if (Dev > 0)
+      	return 0xFFFFFFFF;
+
+      return Base + Address + Offset;
+  }
+  return Base + Offset;
+}
+
+/**
+  Internal worker function to read a PCI configuration register.
+
+  @param  Address The address that encodes the PCI Bus, Device, Function and
+                  Register.
+  @param  Width   The width of data to read
+
+  @return The value read from the PCI configuration register.
+
+**/
+STATIC
+UINT32
+PciSegmentLibReadWorker (
+  IN  UINT64                      Address,
+  IN  PCI_CFG_WIDTH               Width
+  )
+{
+  UINT64    Base;
+  UINT32    Ret;
+
+  EfiAcquireLock (&mPciSegmentReadWriteLock);
+  Base = PciSegmentLibGetConfigBase (Address, 0);
+
+  if (Base == 0xFFFFFFFF) {
+    EfiReleaseLock (&mPciSegmentReadWriteLock);
+    return Base;
+  }
+
+  switch (Width) {
+  case PciCfgWidthUint8:
+    Ret = MmioRead8 (Base);
+    break;
+  case PciCfgWidthUint16:
+    Ret = MmioRead16 (Base);
+    break;
+  case PciCfgWidthUint32:
+    Ret = MmioRead32 (Base);
+    break;
+  default:
+    ASSERT (FALSE);
+    Ret = 0;
+  }
+  EfiReleaseLock (&mPciSegmentReadWriteLock);
+  //DEBUG ((DEBUG_ERROR, "PCIe seg read Address %lx %lx width %d val %x\n", Base, Address, Width, Ret));
+  return Ret;
+}
+
+/**
+  Internal worker function to writes a PCI configuration register.
+
+  @param  Address The address that encodes the PCI Bus, Device, Function and
+                  Register.
+  @param  Width   The width of data to write
+  @param  Data    The value to write.
+
+  @return The value written to the PCI configuration register.
+
+**/
+STATIC
+UINT32
+PciSegmentLibWriteWorker (
+  IN  UINT64                      Address,
+  IN  PCI_CFG_WIDTH               Width,
+  IN  UINT32                      Data
+  )
+{
+  UINT64    Base;
+
+  EfiAcquireLock (&mPciSegmentReadWriteLock);
+  Base = PciSegmentLibGetConfigBase (Address, 1);
+
+  switch (Width) {
+  case PciCfgWidthUint8:
+    MmioWrite8 (Base, Data);
+    break;
+  case PciCfgWidthUint16:
+    MmioWrite16 (Base, Data);
+    break;
+  case PciCfgWidthUint32:
+    MmioWrite32 (Base, Data);
+    break;
+  default:
+    ASSERT (FALSE);
+  }
+  EfiReleaseLock (&mPciSegmentReadWriteLock);
+  return Data;
+}
+
+/**
+  Register a PCI device so PCI configuration registers may be accessed after
+  SetVirtualAddressMap().
+
+  If any reserved bits in Address are set, then ASSERT().
+
+  @param  Address The address that encodes the PCI Bus, Device, Function and
+                  Register.
+
+  @retval RETURN_SUCCESS           The PCI device was registered for runtime access.
+  @retval RETURN_UNSUPPORTED       An attempt was made to call this function
+                                   after ExitBootServices().
+  @retval RETURN_UNSUPPORTED       The resources required to access the PCI device
+                                   at runtime could not be mapped.
+  @retval RETURN_OUT_OF_RESOURCES  There are not enough resources available to
+                                   complete the registration.
+
+**/
+RETURN_STATUS
+EFIAPI
+PciSegmentRegisterForRuntimeAccess (
+  IN UINTN  Address
+  )
+{
+  ASSERT_INVALID_PCI_SEGMENT_ADDRESS (Address, 0);
+  return RETURN_UNSUPPORTED;
+}
+
+/**
+  Reads an 8-bit PCI configuration register.
+
+  Reads and returns the 8-bit PCI configuration register specified by Address.
+  This function must guarantee that all PCI read and write operations are serialized.
+
+  If any reserved bits in Address are set, then ASSERT().
+
+  @param  Address   The address that encodes the PCI Segment, Bus, Device, Function,
+                    and Register.
+
+  @return The 8-bit PCI configuration register specified by Address.
+
+**/
+UINT8
+EFIAPI
+PciSegmentRead8 (
+  IN UINT64                    Address
+  )
+{
+  ASSERT_INVALID_PCI_SEGMENT_ADDRESS (Address, 0);
+
+  return (UINT8) PciSegmentLibReadWorker (Address, PciCfgWidthUint8);
+}
+
+/**
+  Writes an 8-bit PCI configuration register.
+
+  Writes the 8-bit PCI configuration register specified by Address with the value specified by Value.
+  Value is returned.  This function must guarantee that all PCI read and write operations are serialized.
+
+  If any reserved bits in Address are set, then ASSERT().
+
+  @param  Address     The address that encodes the PCI Segment, Bus, Device, Function, and Register.
+  @param  Value       The value to write.
+
+  @return The value written to the PCI configuration register.
+
+**/
+UINT8
+EFIAPI
+PciSegmentWrite8 (
+  IN UINT64                    Address,
+  IN UINT8                     Value
+  )
+{
+  ASSERT_INVALID_PCI_SEGMENT_ADDRESS (Address, 0);
+
+  return (UINT8) PciSegmentLibWriteWorker (Address, PciCfgWidthUint8, Value);
+}
+
+/**
+  Performs a bitwise OR of an 8-bit PCI configuration register with an 8-bit value.
+
+  Reads the 8-bit PCI configuration register specified by Address,
+  performs a bitwise OR between the read result and the value specified by OrData,
+  and writes the result to the 8-bit PCI configuration register specified by Address.
+  The value written to the PCI configuration register is returned.
+  This function must guarantee that all PCI read and write operations are serialized.
+
+  If any reserved bits in Address are set, then ASSERT().
+
+  @param  Address   The address that encodes the PCI Segment, Bus, Device, Function, and Register.
+  @param  OrData    The value to OR with the PCI configuration register.
+
+  @return The value written to the PCI configuration register.
+
+**/
+UINT8
+EFIAPI
+PciSegmentOr8 (
+  IN UINT64                    Address,
+  IN UINT8                     OrData
+  )
+{
+  return PciSegmentWrite8 (Address, (UINT8) (PciSegmentRead8 (Address) | OrData));
+}
+
+/**
+  Performs a bitwise AND of an 8-bit PCI configuration register with an 8-bit value.
+
+  Reads the 8-bit PCI configuration register specified by Address,
+  performs a bitwise AND between the read result and the value specified by AndData,
+  and writes the result to the 8-bit PCI configuration register specified by Address.
+  The value written to the PCI configuration register is returned.
+  This function must guarantee that all PCI read and write operations are serialized.
+  If any reserved bits in Address are set, then ASSERT().
+
+  @param  Address   The address that encodes the PCI Segment, Bus, Device, Function, and Register.
+  @param  AndData   The value to AND with the PCI configuration register.
+
+  @return The value written to the PCI configuration register.
+
+**/
+UINT8
+EFIAPI
+PciSegmentAnd8 (
+  IN UINT64                    Address,
+  IN UINT8                     AndData
+  )
+{
+  return PciSegmentWrite8 (Address, (UINT8) (PciSegmentRead8 (Address) & AndData));
+}
+
+/**
+  Performs a bitwise AND of an 8-bit PCI configuration register with an 8-bit value,
+  followed a  bitwise OR with another 8-bit value.
+
+  Reads the 8-bit PCI configuration register specified by Address,
+  performs a bitwise AND between the read result and the value specified by AndData,
+  performs a bitwise OR between the result of the AND operation and the value specified by OrData,
+  and writes the result to the 8-bit PCI configuration register specified by Address.
+  The value written to the PCI configuration register is returned.
+  This function must guarantee that all PCI read and write operations are serialized.
+
+  If any reserved bits in Address are set, then ASSERT().
+
+  @param  Address   The address that encodes the PCI Segment, Bus, Device, Function, and Register.
+  @param  AndData    The value to AND with the PCI configuration register.
+  @param  OrData    The value to OR with the PCI configuration register.
+
+  @return The value written to the PCI configuration register.
+
+**/
+UINT8
+EFIAPI
+PciSegmentAndThenOr8 (
+  IN UINT64                    Address,
+  IN UINT8                     AndData,
+  IN UINT8                     OrData
+  )
+{
+  return PciSegmentWrite8 (Address, (UINT8) ((PciSegmentRead8 (Address) & AndData) | OrData));
+}
+
+/**
+  Reads a bit field of a PCI configuration register.
+
+  Reads the bit field in an 8-bit PCI configuration register. The bit field is
+  specified by the StartBit and the EndBit. The value of the bit field is
+  returned.
+
+  If any reserved bits in Address are set, then ASSERT().
+  If StartBit is greater than 7, then ASSERT().
+  If EndBit is greater than 7, then ASSERT().
+  If EndBit is less than StartBit, then ASSERT().
+
+  @param  Address   The PCI configuration register to read.
+  @param  StartBit  The ordinal of the least significant bit in the bit field.
+                    Range 0..7.
+  @param  EndBit    The ordinal of the most significant bit in the bit field.
+                    Range 0..7.
+
+  @return The value of the bit field read from the PCI configuration register.
+
+**/
+UINT8
+EFIAPI
+PciSegmentBitFieldRead8 (
+  IN UINT64                    Address,
+  IN UINTN                     StartBit,
+  IN UINTN                     EndBit
+  )
+{
+  return BitFieldRead8 (PciSegmentRead8 (Address), StartBit, EndBit);
+}
+
+/**
+  Writes a bit field to a PCI configuration register.
+
+  Writes Value to the bit field of the PCI configuration register. The bit
+  field is specified by the StartBit and the EndBit. All other bits in the
+  destination PCI configuration register are preserved. The new value of the
+  8-bit register is returned.
+
+  If any reserved bits in Address are set, then ASSERT().
+  If StartBit is greater than 7, then ASSERT().
+  If EndBit is greater than 7, then ASSERT().
+  If EndBit is less than StartBit, then ASSERT().
+  If Value is larger than the bitmask value range specified by StartBit and EndBit, then ASSERT().
+
+  @param  Address   The PCI configuration register to write.
+  @param  StartBit  The ordinal of the least significant bit in the bit field.
+                    Range 0..7.
+  @param  EndBit    The ordinal of the most significant bit in the bit field.
+                    Range 0..7.
+  @param  Value     The new value of the bit field.
+
+  @return The value written back to the PCI configuration register.
+
+**/
+UINT8
+EFIAPI
+PciSegmentBitFieldWrite8 (
+  IN UINT64                    Address,
+  IN UINTN                     StartBit,
+  IN UINTN                     EndBit,
+  IN UINT8                     Value
+  )
+{
+  return PciSegmentWrite8 (
+           Address,
+           BitFieldWrite8 (PciSegmentRead8 (Address), StartBit, EndBit, Value)
+           );
+}
+
+/**
+  Reads a bit field in an 8-bit PCI configuration, performs a bitwise OR, and
+  writes the result back to the bit field in the 8-bit port.
+
+  Reads the 8-bit PCI configuration register specified by Address, performs a
+  bitwise OR between the read result and the value specified by
+  OrData, and writes the result to the 8-bit PCI configuration register
+  specified by Address. The value written to the PCI configuration register is
+  returned. This function must guarantee that all PCI read and write operations
+  are serialized. Extra left bits in OrData are stripped.
+
+  If any reserved bits in Address are set, then ASSERT().
+  If StartBit is greater than 7, then ASSERT().
+  If EndBit is greater than 7, then ASSERT().
+  If EndBit is less than StartBit, then ASSERT().
+  If OrData is larger than the bitmask value range specified by StartBit and EndBit, then ASSERT().
+
+  @param  Address   The PCI configuration register to write.
+  @param  StartBit  The ordinal of the least significant bit in the bit field.
+                    Range 0..7.
+  @param  EndBit    The ordinal of the most significant bit in the bit field.
+                    Range 0..7.
+  @param  OrData    The value to OR with the PCI configuration register.
+
+  @return The value written back to the PCI configuration register.
+
+**/
+UINT8
+EFIAPI
+PciSegmentBitFieldOr8 (
+  IN UINT64                    Address,
+  IN UINTN                     StartBit,
+  IN UINTN                     EndBit,
+  IN UINT8                     OrData
+  )
+{
+  return PciSegmentWrite8 (
+           Address,
+           BitFieldOr8 (PciSegmentRead8 (Address), StartBit, EndBit, OrData)
+           );
+}
+
+/**
+  Reads a bit field in an 8-bit PCI configuration register, performs a bitwise
+  AND, and writes the result back to the bit field in the 8-bit register.
+
+  Reads the 8-bit PCI configuration register specified by Address, performs a
+  bitwise AND between the read result and the value specified by AndData, and
+  writes the result to the 8-bit PCI configuration register specified by
+  Address. The value written to the PCI configuration register is returned.
+  This function must guarantee that all PCI read and write operations are
+  serialized. Extra left bits in AndData are stripped.
+
+  If any reserved bits in Address are set, then ASSERT().
+  If StartBit is greater than 7, then ASSERT().
+  If EndBit is greater than 7, then ASSERT().
+  If EndBit is less than StartBit, then ASSERT().
+  If AndData is larger than the bitmask value range specified by StartBit and EndBit, then ASSERT().
+
+  @param  Address   The PCI configuration register to write.
+  @param  StartBit  The ordinal of the least significant bit in the bit field.
+                    Range 0..7.
+  @param  EndBit    The ordinal of the most significant bit in the bit field.
+                    Range 0..7.
+  @param  AndData   The value to AND with the PCI configuration register.
+
+  @return The value written back to the PCI configuration register.
+
+**/
+UINT8
+EFIAPI
+PciSegmentBitFieldAnd8 (
+  IN UINT64                    Address,
+  IN UINTN                     StartBit,
+  IN UINTN                     EndBit,
+  IN UINT8                     AndData
+  )
+{
+  return PciSegmentWrite8 (
+           Address,
+           BitFieldAnd8 (PciSegmentRead8 (Address), StartBit, EndBit, AndData)
+           );
+}
+
+/**
+  Reads a bit field in an 8-bit port, performs a bitwise AND followed by a
+  bitwise OR, and writes the result back to the bit field in the
+  8-bit port.
+
+  Reads the 8-bit PCI configuration register specified by Address, performs a
+  bitwise AND followed by a bitwise OR between the read result and
+  the value specified by AndData, and writes the result to the 8-bit PCI
+  configuration register specified by Address. The value written to the PCI
+  configuration register is returned. This function must guarantee that all PCI
+  read and write operations are serialized. Extra left bits in both AndData and
+  OrData are stripped.
+
+  If any reserved bits in Address are set, then ASSERT().
+  If StartBit is greater than 7, then ASSERT().
+  If EndBit is greater than 7, then ASSERT().
+  If EndBit is less than StartBit, then ASSERT().
+  If AndData is larger than the bitmask value range specified by StartBit and EndBit, then ASSERT().
+  If OrData is larger than the bitmask value range specified by StartBit and EndBit, then ASSERT().
+
+  @param  Address   The PCI configuration register to write.
+  @param  StartBit  The ordinal of the least significant bit in the bit field.
+                    Range 0..7.
+  @param  EndBit    The ordinal of the most significant bit in the bit field.
+                    Range 0..7.
+  @param  AndData   The value to AND with the PCI configuration register.
+  @param  OrData    The value to OR with the result of the AND operation.
+
+  @return The value written back to the PCI configuration register.
+
+**/
+UINT8
+EFIAPI
+PciSegmentBitFieldAndThenOr8 (
+  IN UINT64                    Address,
+  IN UINTN                     StartBit,
+  IN UINTN                     EndBit,
+  IN UINT8                     AndData,
+  IN UINT8                     OrData
+  )
+{
+  return PciSegmentWrite8 (
+           Address,
+           BitFieldAndThenOr8 (PciSegmentRead8 (Address), StartBit, EndBit, AndData, OrData)
+           );
+}
+
+/**
+  Reads a 16-bit PCI configuration register.
+
+  Reads and returns the 16-bit PCI configuration register specified by Address.
+  This function must guarantee that all PCI read and write operations are serialized.
+
+  If any reserved bits in Address are set, then ASSERT().
+  If Address is not aligned on a 16-bit boundary, then ASSERT().
+
+  @param  Address   The address that encodes the PCI Segment, Bus, Device, Function, and Register.
+
+  @return The 16-bit PCI configuration register specified by Address.
+
+**/
+UINT16
+EFIAPI
+PciSegmentRead16 (
+  IN UINT64                    Address
+  )
+{
+  ASSERT_INVALID_PCI_SEGMENT_ADDRESS (Address, 1);
+
+  return (UINT16) PciSegmentLibReadWorker (Address, PciCfgWidthUint16);
+}
+
+/**
+  Writes a 16-bit PCI configuration register.
+
+  Writes the 16-bit PCI configuration register specified by Address with the value specified by Value.
+  Value is returned.  This function must guarantee that all PCI read and write operations are serialized.
+
+  If any reserved bits in Address are set, then ASSERT().
+  If Address is not aligned on a 16-bit boundary, then ASSERT().
+
+  @param  Address     The address that encodes the PCI Segment, Bus, Device, Function, and Register.
+  @param  Value       The value to write.
+
+  @return The parameter of Value.
+
+**/
+UINT16
+EFIAPI
+PciSegmentWrite16 (
+  IN UINT64                    Address,
+  IN UINT16                    Value
+  )
+{
+  ASSERT_INVALID_PCI_SEGMENT_ADDRESS (Address, 1);
+
+  return (UINT16) PciSegmentLibWriteWorker (Address, PciCfgWidthUint16, Value);
+}
+
+/**
+  Performs a bitwise OR of a 16-bit PCI configuration register with
+  a 16-bit value.
+
+  Reads the 16-bit PCI configuration register specified by Address, performs a
+  bitwise OR between the read result and the value specified by
+  OrData, and writes the result to the 16-bit PCI configuration register
+  specified by Address. The value written to the PCI configuration register is
+  returned. This function must guarantee that all PCI read and write operations
+  are serialized.
+
+  If any reserved bits in Address are set, then ASSERT().
+  If Address is not aligned on a 16-bit boundary, then ASSERT().
+
+  @param  Address The address that encodes the PCI Segment, Bus, Device, Function and
+                  Register.
+  @param  OrData  The value to OR with the PCI configuration register.
+
+  @return The value written back to the PCI configuration register.
+
+**/
+UINT16
+EFIAPI
+PciSegmentOr16 (
+  IN UINT64                    Address,
+  IN UINT16                    OrData
+  )
+{
+  return PciSegmentWrite16 (Address, (UINT16) (PciSegmentRead16 (Address) | OrData));
+}
+
+/**
+  Performs a bitwise AND of a 16-bit PCI configuration register with a 16-bit value.
+
+  Reads the 16-bit PCI configuration register specified by Address,
+  performs a bitwise AND between the read result and the value specified by AndData,
+  and writes the result to the 16-bit PCI configuration register specified by Address.
+  The value written to the PCI configuration register is returned.
+  This function must guarantee that all PCI read and write operations are serialized.
+
+  If any reserved bits in Address are set, then ASSERT().
+  If Address is not aligned on a 16-bit boundary, then ASSERT().
+
+  @param  Address   The address that encodes the PCI Segment, Bus, Device, Function, and Register.
+  @param  AndData   The value to AND with the PCI configuration register.
+
+  @return The value written to the PCI configuration register.
+
+**/
+UINT16
+EFIAPI
+PciSegmentAnd16 (
+  IN UINT64                    Address,
+  IN UINT16                    AndData
+  )
+{
+  return PciSegmentWrite16 (Address, (UINT16) (PciSegmentRead16 (Address) & AndData));
+}
+
+/**
+  Performs a bitwise AND of a 16-bit PCI configuration register with a 16-bit value,
+  followed a  bitwise OR with another 16-bit value.
+
+  Reads the 16-bit PCI configuration register specified by Address,
+  performs a bitwise AND between the read result and the value specified by AndData,
+  performs a bitwise OR between the result of the AND operation and the value specified by OrData,
+  and writes the result to the 16-bit PCI configuration register specified by Address.
+  The value written to the PCI configuration register is returned.
+  This function must guarantee that all PCI read and write operations are serialized.
+
+  If any reserved bits in Address are set, then ASSERT().
+  If Address is not aligned on a 16-bit boundary, then ASSERT().
+
+  @param  Address   The address that encodes the PCI Segment, Bus, Device, Function, and Register.
+  @param  AndData   The value to AND with the PCI configuration register.
+  @param  OrData    The value to OR with the PCI configuration register.
+
+  @return The value written to the PCI configuration register.
+
+**/
+UINT16
+EFIAPI
+PciSegmentAndThenOr16 (
+  IN UINT64                    Address,
+  IN UINT16                    AndData,
+  IN UINT16                    OrData
+  )
+{
+  return PciSegmentWrite16 (Address, (UINT16) ((PciSegmentRead16 (Address) & AndData) | OrData));
+}
+
+/**
+  Reads a bit field of a PCI configuration register.
+
+  Reads the bit field in a 16-bit PCI configuration register. The bit field is
+  specified by the StartBit and the EndBit. The value of the bit field is
+  returned.
+
+  If any reserved bits in Address are set, then ASSERT().
+  If Address is not aligned on a 16-bit boundary, then ASSERT().
+  If StartBit is greater than 15, then ASSERT().
+  If EndBit is greater than 15, then ASSERT().
+  If EndBit is less than StartBit, then ASSERT().
+
+  @param  Address   The PCI configuration register to read.
+  @param  StartBit  The ordinal of the least significant bit in the bit field.
+                    Range 0..15.
+  @param  EndBit    The ordinal of the most significant bit in the bit field.
+                    Range 0..15.
+
+  @return The value of the bit field read from the PCI configuration register.
+
+**/
+UINT16
+EFIAPI
+PciSegmentBitFieldRead16 (
+  IN UINT64                    Address,
+  IN UINTN                     StartBit,
+  IN UINTN                     EndBit
+  )
+{
+  return BitFieldRead16 (PciSegmentRead16 (Address), StartBit, EndBit);
+}
+
+/**
+  Writes a bit field to a PCI configuration register.
+
+  Writes Value to the bit field of the PCI configuration register. The bit
+  field is specified by the StartBit and the EndBit. All other bits in the
+  destination PCI configuration register are preserved. The new value of the
+  16-bit register is returned.
+
+  If any reserved bits in Address are set, then ASSERT().
+  If Address is not aligned on a 16-bit boundary, then ASSERT().
+  If StartBit is greater than 15, then ASSERT().
+  If EndBit is greater than 15, then ASSERT().
+  If EndBit is less than StartBit, then ASSERT().
+  If Value is larger than the bitmask value range specified by StartBit and EndBit, then ASSERT().
+
+  @param  Address   The PCI configuration register to write.
+  @param  StartBit  The ordinal of the least significant bit in the bit field.
+                    Range 0..15.
+  @param  EndBit    The ordinal of the most significant bit in the bit field.
+                    Range 0..15.
+  @param  Value     The new value of the bit field.
+
+  @return The value written back to the PCI configuration register.
+
+**/
+UINT16
+EFIAPI
+PciSegmentBitFieldWrite16 (
+  IN UINT64                    Address,
+  IN UINTN                     StartBit,
+  IN UINTN                     EndBit,
+  IN UINT16                    Value
+  )
+{
+  return PciSegmentWrite16 (
+           Address,
+           BitFieldWrite16 (PciSegmentRead16 (Address), StartBit, EndBit, Value)
+           );
+}
+
+/**
+  Reads the 16-bit PCI configuration register specified by Address,
+  performs a bitwise OR between the read result and the value specified by OrData,
+  and writes the result to the 16-bit PCI configuration register specified by Address.
+
+  If any reserved bits in Address are set, then ASSERT().
+  If Address is not aligned on a 16-bit boundary, then ASSERT().
+  If StartBit is greater than 15, then ASSERT().
+  If EndBit is greater than 15, then ASSERT().
+  If EndBit is less than StartBit, then ASSERT().
+  If OrData is larger than the bitmask value range specified by StartBit and EndBit, then ASSERT().
+
+  @param  Address   The PCI configuration register to write.
+  @param  StartBit  The ordinal of the least significant bit in the bit field.
+                    Range 0..15.
+  @param  EndBit    The ordinal of the most significant bit in the bit field.
+                    Range 0..15.
+  @param  OrData    The value to OR with the PCI configuration register.
+
+  @return The value written back to the PCI configuration register.
+
+**/
+UINT16
+EFIAPI
+PciSegmentBitFieldOr16 (
+  IN UINT64                    Address,
+  IN UINTN                     StartBit,
+  IN UINTN                     EndBit,
+  IN UINT16                    OrData
+  )
+{
+  return PciSegmentWrite16 (
+           Address,
+           BitFieldOr16 (PciSegmentRead16 (Address), StartBit, EndBit, OrData)
+           );
+}
+
+/**
+  Reads a bit field in a 16-bit PCI configuration, performs a bitwise OR,
+  and writes the result back to the bit field in the 16-bit port.
+
+  Reads the 16-bit PCI configuration register specified by Address,
+  performs a bitwise OR between the read result and the value specified by OrData,
+  and writes the result to the 16-bit PCI configuration register specified by Address.
+  The value written to the PCI configuration register is returned.
+  This function must guarantee that all PCI read and write operations are serialized.
+  Extra left bits in OrData are stripped.
+
+  If any reserved bits in Address are set, then ASSERT().
+  If Address is not aligned on a 16-bit boundary, then ASSERT().
+  If StartBit is greater than 7, then ASSERT().
+  If EndBit is greater than 7, then ASSERT().
+  If EndBit is less than StartBit, then ASSERT().
+  If AndData is larger than the bitmask value range specified by StartBit and EndBit, then ASSERT().
+
+  @param  Address   The address that encodes the PCI Segment, Bus, Device, Function, and Register.
+  @param  StartBit  The ordinal of the least significant bit in the bit field.
+                    The ordinal of the least significant bit in a byte is bit 0.
+  @param  EndBit    The ordinal of the most significant bit in the bit field.
+                    The ordinal of the most significant bit in a byte is bit 7.
+  @param  AndData   The value to AND with the read value from the PCI configuration register.
+
+  @return The value written to the PCI configuration register.
+
+**/
+UINT16
+EFIAPI
+PciSegmentBitFieldAnd16 (
+  IN UINT64                    Address,
+  IN UINTN                     StartBit,
+  IN UINTN                     EndBit,
+  IN UINT16                    AndData
+  )
+{
+  return PciSegmentWrite16 (
+           Address,
+           BitFieldAnd16 (PciSegmentRead16 (Address), StartBit, EndBit, AndData)
+           );
+}
+
+/**
+  Reads a bit field in a 16-bit port, performs a bitwise AND followed by a
+  bitwise OR, and writes the result back to the bit field in the
+  16-bit port.
+
+  Reads the 16-bit PCI configuration register specified by Address, performs a
+  bitwise AND followed by a bitwise OR between the read result and
+  the value specified by AndData, and writes the result to the 16-bit PCI
+  configuration register specified by Address. The value written to the PCI
+  configuration register is returned. This function must guarantee that all PCI
+  read and write operations are serialized. Extra left bits in both AndData and
+  OrData are stripped.
+
+  If any reserved bits in Address are set, then ASSERT().
+  If StartBit is greater than 15, then ASSERT().
+  If EndBit is greater than 15, then ASSERT().
+  If EndBit is less than StartBit, then ASSERT().
+  If AndData is larger than the bitmask value range specified by StartBit and EndBit, then ASSERT().
+  If OrData is larger than the bitmask value range specified by StartBit and EndBit, then ASSERT().
+
+  @param  Address   The PCI configuration register to write.
+  @param  StartBit  The ordinal of the least significant bit in the bit field.
+                    Range 0..15.
+  @param  EndBit    The ordinal of the most significant bit in the bit field.
+                    Range 0..15.
+  @param  AndData   The value to AND with the PCI configuration register.
+  @param  OrData    The value to OR with the result of the AND operation.
+
+  @return The value written back to the PCI configuration register.
+
+**/
+UINT16
+EFIAPI
+PciSegmentBitFieldAndThenOr16 (
+  IN UINT64                    Address,
+  IN UINTN                     StartBit,
+  IN UINTN                     EndBit,
+  IN UINT16                    AndData,
+  IN UINT16                    OrData
+  )
+{
+  return PciSegmentWrite16 (
+           Address,
+           BitFieldAndThenOr16 (PciSegmentRead16 (Address), StartBit, EndBit, AndData, OrData)
+           );
+}
+
+/**
+  Reads a 32-bit PCI configuration register.
+
+  Reads and returns the 32-bit PCI configuration register specified by Address.
+  This function must guarantee that all PCI read and write operations are serialized.
+
+  If any reserved bits in Address are set, then ASSERT().
+  If Address is not aligned on a 32-bit boundary, then ASSERT().
+
+  @param  Address   The address that encodes the PCI Segment, Bus, Device, Function,
+                    and Register.
+
+  @return The 32-bit PCI configuration register specified by Address.
+
+**/
+UINT32
+EFIAPI
+PciSegmentRead32 (
+  IN UINT64                    Address
+  )
+{
+  ASSERT_INVALID_PCI_SEGMENT_ADDRESS (Address, 3);
+
+  return PciSegmentLibReadWorker (Address, PciCfgWidthUint32);
+}
+
+/**
+  Writes a 32-bit PCI configuration register.
+
+  Writes the 32-bit PCI configuration register specified by Address with the value specified by Value.
+  Value is returned.  This function must guarantee that all PCI read and write operations are serialized.
+
+  If any reserved bits in Address are set, then ASSERT().
+  If Address is not aligned on a 32-bit boundary, then ASSERT().
+
+  @param  Address     The address that encodes the PCI Segment, Bus, Device,
+                      Function, and Register.
+  @param  Value       The value to write.
+
+  @return The parameter of Value.
+
+**/
+UINT32
+EFIAPI
+PciSegmentWrite32 (
+  IN UINT64                    Address,
+  IN UINT32                    Value
+  )
+{
+  ASSERT_INVALID_PCI_SEGMENT_ADDRESS (Address, 3);
+
+  return PciSegmentLibWriteWorker (Address, PciCfgWidthUint32, Value);
+}
+
+/**
+  Performs a bitwise OR of a 32-bit PCI configuration register with a 32-bit value.
+
+  Reads the 32-bit PCI configuration register specified by Address,
+  performs a bitwise OR between the read result and the value specified by OrData,
+  and writes the result to the 32-bit PCI configuration register specified by Address.
+  The value written to the PCI configuration register is returned.
+  This function must guarantee that all PCI read and write operations are serialized.
+
+  If any reserved bits in Address are set, then ASSERT().
+  If Address is not aligned on a 32-bit boundary, then ASSERT().
+
+  @param  Address   The address that encodes the PCI Segment, Bus, Device, Function, and Register.
+  @param  OrData    The value to OR with the PCI configuration register.
+
+  @return The value written to the PCI configuration register.
+
+**/
+UINT32
+EFIAPI
+PciSegmentOr32 (
+  IN UINT64                    Address,
+  IN UINT32                    OrData
+  )
+{
+  return PciSegmentWrite32 (Address, PciSegmentRead32 (Address) | OrData);
+}
+
+/**
+  Performs a bitwise AND of a 32-bit PCI configuration register with a 32-bit value.
+
+  Reads the 32-bit PCI configuration register specified by Address,
+  performs a bitwise AND between the read result and the value specified by AndData,
+  and writes the result to the 32-bit PCI configuration register specified by Address.
+  The value written to the PCI configuration register is returned.
+  This function must guarantee that all PCI read and write operations are serialized.
+
+  If any reserved bits in Address are set, then ASSERT().
+  If Address is not aligned on a 32-bit boundary, then ASSERT().
+
+  @param  Address   The address that encodes the PCI Segment, Bus, Device, Function,
+                    and Register.
+  @param  AndData   The value to AND with the PCI configuration register.
+
+  @return The value written to the PCI configuration register.
+
+**/
+UINT32
+EFIAPI
+PciSegmentAnd32 (
+  IN UINT64                    Address,
+  IN UINT32                    AndData
+  )
+{
+  return PciSegmentWrite32 (Address, PciSegmentRead32 (Address) & AndData);
+}
+
+/**
+  Performs a bitwise AND of a 32-bit PCI configuration register with a 32-bit value,
+  followed a  bitwise OR with another 32-bit value.
+
+  Reads the 32-bit PCI configuration register specified by Address,
+  performs a bitwise AND between the read result and the value specified by AndData,
+  performs a bitwise OR between the result of the AND operation and the value specified by OrData,
+  and writes the result to the 32-bit PCI configuration register specified by Address.
+  The value written to the PCI configuration register is returned.
+  This function must guarantee that all PCI read and write operations are serialized.
+
+  If any reserved bits in Address are set, then ASSERT().
+  If Address is not aligned on a 32-bit boundary, then ASSERT().
+
+  @param  Address   The address that encodes the PCI Segment, Bus, Device, Function,
+                    and Register.
+  @param  AndData   The value to AND with the PCI configuration register.
+  @param  OrData    The value to OR with the PCI configuration register.
+
+  @return The value written to the PCI configuration register.
+
+**/
+UINT32
+EFIAPI
+PciSegmentAndThenOr32 (
+  IN UINT64                    Address,
+  IN UINT32                    AndData,
+  IN UINT32                    OrData
+  )
+{
+  return PciSegmentWrite32 (Address, (PciSegmentRead32 (Address) & AndData) | OrData);
+}
+
+/**
+  Reads a bit field of a PCI configuration register.
+
+  Reads the bit field in a 32-bit PCI configuration register. The bit field is
+  specified by the StartBit and the EndBit. The value of the bit field is
+  returned.
+
+  If any reserved bits in Address are set, then ASSERT().
+  If Address is not aligned on a 32-bit boundary, then ASSERT().
+  If StartBit is greater than 31, then ASSERT().
+  If EndBit is greater than 31, then ASSERT().
+  If EndBit is less than StartBit, then ASSERT().
+
+  @param  Address   The PCI configuration register to read.
+  @param  StartBit  The ordinal of the least significant bit in the bit field.
+                    Range 0..31.
+  @param  EndBit    The ordinal of the most significant bit in the bit field.
+                    Range 0..31.
+
+  @return The value of the bit field read from the PCI configuration register.
+
+**/
+UINT32
+EFIAPI
+PciSegmentBitFieldRead32 (
+  IN UINT64                    Address,
+  IN UINTN                     StartBit,
+  IN UINTN                     EndBit
+  )
+{
+  return BitFieldRead32 (PciSegmentRead32 (Address), StartBit, EndBit);
+}
+
+/**
+  Writes a bit field to a PCI configuration register.
+
+  Writes Value to the bit field of the PCI configuration register. The bit
+  field is specified by the StartBit and the EndBit. All other bits in the
+  destination PCI configuration register are preserved. The new value of the
+  32-bit register is returned.
+
+  If any reserved bits in Address are set, then ASSERT().
+  If Address is not aligned on a 32-bit boundary, then ASSERT().
+  If StartBit is greater than 31, then ASSERT().
+  If EndBit is greater than 31, then ASSERT().
+  If EndBit is less than StartBit, then ASSERT().
+  If Value is larger than the bitmask value range specified by StartBit and EndBit, then ASSERT().
+
+  @param  Address   The PCI configuration register to write.
+  @param  StartBit  The ordinal of the least significant bit in the bit field.
+                    Range 0..31.
+  @param  EndBit    The ordinal of the most significant bit in the bit field.
+                    Range 0..31.
+  @param  Value     The new value of the bit field.
+
+  @return The value written back to the PCI configuration register.
+
+**/
+UINT32
+EFIAPI
+PciSegmentBitFieldWrite32 (
+  IN UINT64                    Address,
+  IN UINTN                     StartBit,
+  IN UINTN                     EndBit,
+  IN UINT32                    Value
+  )
+{
+  return PciSegmentWrite32 (
+           Address,
+           BitFieldWrite32 (PciSegmentRead32 (Address), StartBit, EndBit, Value)
+           );
+}
+
+/**
+  Reads a bit field in a 32-bit PCI configuration, performs a bitwise OR, and
+  writes the result back to the bit field in the 32-bit port.
+
+  Reads the 32-bit PCI configuration register specified by Address, performs a
+  bitwise OR between the read result and the value specified by
+  OrData, and writes the result to the 32-bit PCI configuration register
+  specified by Address. The value written to the PCI configuration register is
+  returned. This function must guarantee that all PCI read and write operations
+  are serialized. Extra left bits in OrData are stripped.
+
+  If any reserved bits in Address are set, then ASSERT().
+  If StartBit is greater than 31, then ASSERT().
+  If EndBit is greater than 31, then ASSERT().
+  If EndBit is less than StartBit, then ASSERT().
+  If OrData is larger than the bitmask value range specified by StartBit and EndBit, then ASSERT().
+
+  @param  Address   The PCI configuration register to write.
+  @param  StartBit  The ordinal of the least significant bit in the bit field.
+                    Range 0..31.
+  @param  EndBit    The ordinal of the most significant bit in the bit field.
+                    Range 0..31.
+  @param  OrData    The value to OR with the PCI configuration register.
+
+  @return The value written back to the PCI configuration register.
+
+**/
+UINT32
+EFIAPI
+PciSegmentBitFieldOr32 (
+  IN UINT64                    Address,
+  IN UINTN                     StartBit,
+  IN UINTN                     EndBit,
+  IN UINT32                    OrData
+  )
+{
+  return PciSegmentWrite32 (
+           Address,
+           BitFieldOr32 (PciSegmentRead32 (Address), StartBit, EndBit, OrData)
+           );
+}
+
+/**
+  Reads a bit field in a 32-bit PCI configuration register, performs a bitwise
+  AND, and writes the result back to the bit field in the 32-bit register.
+
+
+  Reads the 32-bit PCI configuration register specified by Address, performs a bitwise
+  AND between the read result and the value specified by AndData, and writes the result
+  to the 32-bit PCI configuration register specified by Address. The value written to
+  the PCI configuration register is returned.  This function must guarantee that all PCI
+  read and write operations are serialized.  Extra left bits in AndData are stripped.
+  If any reserved bits in Address are set, then ASSERT().
+  If Address is not aligned on a 32-bit boundary, then ASSERT().
+  If StartBit is greater than 31, then ASSERT().
+  If EndBit is greater than 31, then ASSERT().
+  If EndBit is less than StartBit, then ASSERT().
+  If AndData is larger than the bitmask value range specified by StartBit and EndBit, then ASSERT().
+
+  @param  Address   The PCI configuration register to write.
+  @param  StartBit  The ordinal of the least significant bit in the bit field.
+                    Range 0..31.
+  @param  EndBit    The ordinal of the most significant bit in the bit field.
+                    Range 0..31.
+  @param  AndData   The value to AND with the PCI configuration register.
+
+  @return The value written back to the PCI configuration register.
+
+**/
+UINT32
+EFIAPI
+PciSegmentBitFieldAnd32 (
+  IN UINT64                    Address,
+  IN UINTN                     StartBit,
+  IN UINTN                     EndBit,
+  IN UINT32                    AndData
+  )
+{
+  return PciSegmentWrite32 (
+           Address,
+           BitFieldAnd32 (PciSegmentRead32 (Address), StartBit, EndBit, AndData)
+           );
+}
+
+/**
+  Reads a bit field in a 32-bit port, performs a bitwise AND followed by a
+  bitwise OR, and writes the result back to the bit field in the
+  32-bit port.
+
+  Reads the 32-bit PCI configuration register specified by Address, performs a
+  bitwise AND followed by a bitwise OR between the read result and
+  the value specified by AndData, and writes the result to the 32-bit PCI
+  configuration register specified by Address. The value written to the PCI
+  configuration register is returned. This function must guarantee that all PCI
+  read and write operations are serialized. Extra left bits in both AndData and
+  OrData are stripped.
+
+  If any reserved bits in Address are set, then ASSERT().
+  If StartBit is greater than 31, then ASSERT().
+  If EndBit is greater than 31, then ASSERT().
+  If EndBit is less than StartBit, then ASSERT().
+  If AndData is larger than the bitmask value range specified by StartBit and EndBit, then ASSERT().
+  If OrData is larger than the bitmask value range specified by StartBit and EndBit, then ASSERT().
+
+  @param  Address   The PCI configuration register to write.
+  @param  StartBit  The ordinal of the least significant bit in the bit field.
+                    Range 0..31.
+  @param  EndBit    The ordinal of the most significant bit in the bit field.
+                    Range 0..31.
+  @param  AndData   The value to AND with the PCI configuration register.
+  @param  OrData    The value to OR with the result of the AND operation.
+
+  @return The value written back to the PCI configuration register.
+
+**/
+UINT32
+EFIAPI
+PciSegmentBitFieldAndThenOr32 (
+  IN UINT64                    Address,
+  IN UINTN                     StartBit,
+  IN UINTN                     EndBit,
+  IN UINT32                    AndData,
+  IN UINT32                    OrData
+  )
+{
+  return PciSegmentWrite32 (
+           Address,
+           BitFieldAndThenOr32 (PciSegmentRead32 (Address), StartBit, EndBit, AndData, OrData)
+           );
+}
+
+/**
+  Reads a range of PCI configuration registers into a caller supplied buffer.
+
+  Reads the range of PCI configuration registers specified by StartAddress and
+  Size into the buffer specified by Buffer. This function only allows the PCI
+  configuration registers from a single PCI function to be read. Size is
+  returned. When possible 32-bit PCI configuration read cycles are used to read
+  from StartAdress to StartAddress + Size. Due to alignment restrictions, 8-bit
+  and 16-bit PCI configuration read cycles may be used at the beginning and the
+  end of the range.
+
+  If any reserved bits in StartAddress are set, then ASSERT().
+  If ((StartAddress & 0xFFF) + Size) > 0x1000, then ASSERT().
+  If Size > 0 and Buffer is NULL, then ASSERT().
+
+  @param  StartAddress  The starting address that encodes the PCI Segment, Bus,
+                        Device, Function and Register.
+  @param  Size          The size in bytes of the transfer.
+  @param  Buffer        The pointer to a buffer receiving the data read.
+
+  @return Size
+
+**/
+UINTN
+EFIAPI
+PciSegmentReadBuffer (
+  IN  UINT64                   StartAddress,
+  IN  UINTN                    Size,
+  OUT VOID                     *Buffer
+  )
+{
+  UINTN                             ReturnValue;
+
+  ASSERT_INVALID_PCI_SEGMENT_ADDRESS (StartAddress, 0);
+  ASSERT (((StartAddress & 0xFFF) + Size) <= 0x1000);
+
+  if (Size == 0) {
+    return Size;
+  }
+
+  ASSERT (Buffer != NULL);
+
+  //
+  // Save Size for return
+  //
+  ReturnValue = Size;
+
+  if ((StartAddress & BIT0) != 0) {
+    //
+    // Read a byte if StartAddress is byte aligned
+    //
+    *(volatile UINT8 *)Buffer = PciSegmentRead8 (StartAddress);
+    StartAddress += sizeof (UINT8);
+    Size -= sizeof (UINT8);
+    Buffer = (UINT8*)Buffer + 1;
+  }
+
+  if (Size >= sizeof (UINT16) && (StartAddress & BIT1) != 0) {
+    //
+    // Read a word if StartAddress is word aligned
+    //
+    WriteUnaligned16 (Buffer, PciSegmentRead16 (StartAddress));
+    StartAddress += sizeof (UINT16);
+    Size -= sizeof (UINT16);
+    Buffer = (UINT16*)Buffer + 1;
+  }
+
+  while (Size >= sizeof (UINT32)) {
+    //
+    // Read as many double words as possible
+    //
+    WriteUnaligned32 (Buffer, PciSegmentRead32 (StartAddress));
+    StartAddress += sizeof (UINT32);
+    Size -= sizeof (UINT32);
+    Buffer = (UINT32*)Buffer + 1;
+  }
+
+  if (Size >= sizeof (UINT16)) {
+    //
+    // Read the last remaining word if exist
+    //
+    WriteUnaligned16 (Buffer, PciSegmentRead16 (StartAddress));
+    StartAddress += sizeof (UINT16);
+    Size -= sizeof (UINT16);
+    Buffer = (UINT16*)Buffer + 1;
+  }
+
+  if (Size >= sizeof (UINT8)) {
+    //
+    // Read the last remaining byte if exist
+    //
+    *(volatile UINT8 *)Buffer = PciSegmentRead8 (StartAddress);
+  }
+
+  return ReturnValue;
+}
+
+
+/**
+  Copies the data in a caller supplied buffer to a specified range of PCI
+  configuration space.
+
+  Writes the range of PCI configuration registers specified by StartAddress and
+  Size from the buffer specified by Buffer. This function only allows the PCI
+  configuration registers from a single PCI function to be written. Size is
+  returned. When possible 32-bit PCI configuration write cycles are used to
+  write from StartAdress to StartAddress + Size. Due to alignment restrictions,
+  8-bit and 16-bit PCI configuration write cycles may be used at the beginning
+  and the end of the range.
+
+  If any reserved bits in StartAddress are set, then ASSERT().
+  If ((StartAddress & 0xFFF) + Size) > 0x1000, then ASSERT().
+  If Size > 0 and Buffer is NULL, then ASSERT().
+
+  @param  StartAddress  The starting address that encodes the PCI Segment, Bus,
+                        Device, Function and Register.
+  @param  Size          The size in bytes of the transfer.
+  @param  Buffer        The pointer to a buffer containing the data to write.
+
+  @return The parameter of Size.
+
+**/
+UINTN
+EFIAPI
+PciSegmentWriteBuffer (
+  IN UINT64                    StartAddress,
+  IN UINTN                     Size,
+  IN VOID                      *Buffer
+  )
+{
+  UINTN                             ReturnValue;
+
+  ASSERT_INVALID_PCI_SEGMENT_ADDRESS (StartAddress, 0);
+  ASSERT (((StartAddress & 0xFFF) + Size) <= 0x1000);
+
+  if (Size == 0) {
+    return 0;
+  }
+
+  ASSERT (Buffer != NULL);
+
+  // The Bcm/Rpi has a single cfg which can be mapped
+  // to any given device on the bus, which means we need to remap
+  // it basically everytime a new config access is done
+
+  //
+  // Save Size for return
+  //
+  ReturnValue = Size;
+
+  if ((StartAddress & BIT0) != 0) {
+    //
+    // Write a byte if StartAddress is byte aligned
+    //
+    PciSegmentWrite8 (StartAddress, *(UINT8*)Buffer);
+    StartAddress += sizeof (UINT8);
+    Size -= sizeof (UINT8);
+    Buffer = (UINT8*)Buffer + 1;
+  }
+
+  if (Size >= sizeof (UINT16) && (StartAddress & BIT1) != 0) {
+    //
+    // Write a word if StartAddress is word aligned
+    //
+    PciSegmentWrite16 (StartAddress, ReadUnaligned16 (Buffer));
+    StartAddress += sizeof (UINT16);
+    Size -= sizeof (UINT16);
+    Buffer = (UINT16*)Buffer + 1;
+  }
+
+  while (Size >= sizeof (UINT32)) {
+    //
+    // Write as many double words as possible
+    //
+    PciSegmentWrite32 (StartAddress, ReadUnaligned32 (Buffer));
+    StartAddress += sizeof (UINT32);
+    Size -= sizeof (UINT32);
+    Buffer = (UINT32*)Buffer + 1;
+  }
+
+  if (Size >= sizeof (UINT16)) {
+    //
+    // Write the last remaining word if exist
+    //
+    PciSegmentWrite16 (StartAddress, ReadUnaligned16 (Buffer));
+    StartAddress += sizeof (UINT16);
+    Size -= sizeof (UINT16);
+    Buffer = (UINT16*)Buffer + 1;
+  }
+
+  if (Size >= sizeof (UINT8)) {
+    //
+    // Write the last remaining byte if exist
+    //
+    PciSegmentWrite8 (StartAddress, *(UINT8*)Buffer);
+  }
+
+  return ReturnValue;
+}

+ 34 - 0
Silicon/StarFive/JH7110/Library/PciSegmentLib/PciSegmentLib.inf

@@ -0,0 +1,34 @@
+## @file
+# PCI Segment Library for StarFive JH7110 SoC
+#
+# Copyright (c) 2023, Minda Chen <minda.chen@starfivetech.com>
+# Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x0001001B
+  BASE_NAME                      = PciSegmentLib
+  FILE_GUID                      = 74fe3f9e-0040-11ea-a7bf-5254005675a0
+  MODULE_TYPE                    = BASE
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = PciSegmentLib
+
+[Sources]
+  PciSegmentLib.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  Silicon/StarFive/JH7110/JH7110.dec
+
+[LibraryClasses]
+  BaseLib
+  DebugLib
+  IoLib
+  PcdLib
+  UefiLib
+
+[FixedPcd]
+  gJH7110TokenSpaceGuid.PcdJH7110PciConfigRegBase