soc/amd/common/data_fabric: read PCI bus decoding from DF registers

The data fabric also controls which PCI bus numbers get decoded to the
PCI root. In order for the resource allocator to know how the hardware
is configured, read the corresponding data fabric registers to get the
information that then gets passed to the allocator.

Picasso, Cezanne, Mendocino and Rembrandt only support one PCI segment
with 256 buses while the Phoenix and Glinda data fabric hardware has
support for more PCI segments. Due to this change, the register layout
is different and incompatible between those two, so introduce the
SOC_AMD_COMMON_BLOCK_DATA_FABRIC_MULTI_PCI_SEGMENT Kconfig option for a
SoC to specify which implementation is needed. At the moment, coreboot
doesn't have support for multiple PCI segments and the code doesn't
support PCI segments other than segment 0.

On Picasso the PCI bus number limit read back from the data fabric
register is 255 even though CONFIG_ECAM_MMCONF_BUS_NUMBER is set to 64,
so also make sure that the bus and limit returned by
data_fabric_get_pci_bus_numbers is within the expected limits.

TEST=PCI bus allocation still works on Mandolin (Picasso) and Birman
(Phoenix). Picasso has 64 PCI buses. coreboot puts this info into the
resource producer in _SB\PCI0\_CRS which the Linux kernel reads:
* coreboot:  PCI0 _CRS: adding busses [0-3f]
* Linux:     pci_bus 0000:00: root bus resource [bus 00-3f]
This matches the information in the ACPI MCFG table.

Signed-off-by: Felix Held <felix-coreboot@felixheld.de>
Change-Id: Ide5fa9b3e95cfd59232048910cc8feacb6dbdb94
Reviewed-on: https://review.coreboot.org/c/coreboot/+/77080
Reviewed-by: Martin L Roth <gaumless@gmail.com>
Reviewed-by: Eric Lai <eric_lai@quanta.corp-partner.google.com>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
diff --git a/src/soc/amd/common/block/data_fabric/Kconfig b/src/soc/amd/common/block/data_fabric/Kconfig
index 4c4f7b3..ba771c6 100644
--- a/src/soc/amd/common/block/data_fabric/Kconfig
+++ b/src/soc/amd/common/block/data_fabric/Kconfig
@@ -13,3 +13,14 @@
 	  resource allocator about the MMIO regions configured in the data
 	  fabric registers so that it knows in which regions it can properly
 	  allocate the non-fixed MMIO devices.
+
+config SOC_AMD_COMMON_BLOCK_DATA_FABRIC_MULTI_PCI_SEGMENT
+	bool
+	depends on SOC_AMD_COMMON_BLOCK_DATA_FABRIC_DOMAIN
+	help
+	  Some AMD SoCs support more than one PCI segment with 256 buses. Those
+	  SoCs however have a different data fabric register layout for the PCI
+	  bus number decoding. SoCs that use a data fabric register pair for
+	  the PCI bus number which includes the segment number must select this
+	  option; SoCs that use one data fabric register for the PCI bus number
+	  which doesn't include a segment number field mustn't select this.
diff --git a/src/soc/amd/common/block/data_fabric/Makefile.inc b/src/soc/amd/common/block/data_fabric/Makefile.inc
index 868df82..f01c70d 100644
--- a/src/soc/amd/common/block/data_fabric/Makefile.inc
+++ b/src/soc/amd/common/block/data_fabric/Makefile.inc
@@ -2,3 +2,9 @@
 
 ramstage-$(CONFIG_SOC_AMD_COMMON_BLOCK_DATA_FABRIC) += data_fabric_helper.c
 ramstage-$(CONFIG_SOC_AMD_COMMON_BLOCK_DATA_FABRIC_DOMAIN) += domain.c
+
+ifeq ($(CONFIG_SOC_AMD_COMMON_BLOCK_DATA_FABRIC_MULTI_PCI_SEGMENT),y)
+ramstage-$(CONFIG_SOC_AMD_COMMON_BLOCK_DATA_FABRIC_DOMAIN) += pci_segment_multi.c
+else
+ramstage-$(CONFIG_SOC_AMD_COMMON_BLOCK_DATA_FABRIC_DOMAIN) += pci_segment_single.c
+endif # !CONFIG_SOC_AMD_COMMON_BLOCK_APOB_NV_DISABLE
diff --git a/src/soc/amd/common/block/data_fabric/domain.c b/src/soc/amd/common/block/data_fabric/domain.c
index 88b1160..d693879 100644
--- a/src/soc/amd/common/block/data_fabric/domain.c
+++ b/src/soc/amd/common/block/data_fabric/domain.c
@@ -16,10 +16,15 @@
 {
 	uint8_t bus, limit;
 
-	/* TODO: Systems with more than one PCI root need to read the data fabric registers to
-	   see which PCI bus numbers get decoded to which PCI root. */
-	bus = 0;
-	limit = CONFIG_ECAM_MMCONF_BUS_NUMBER - 1;
+	if (data_fabric_get_pci_bus_numbers(domain, &bus, &limit) != CB_SUCCESS) {
+		printk(BIOS_ERR, "No PCI bus numbers decoded to PCI root.\n");
+		return;
+	}
+
+	/* TODO: Check if bus >= CONFIG_ECAM_MMCONF_BUS_NUMBER and return in that case */
+
+	/* Make sure to not report more than CONFIG_ECAM_MMCONF_BUS_NUMBER PCI buses */
+	limit = MIN(limit, CONFIG_ECAM_MMCONF_BUS_NUMBER - 1);
 
 	/* Set bus first number of PCI root */
 	domain->link_list->secondary = bus;
diff --git a/src/soc/amd/common/block/data_fabric/pci_segment_multi.c b/src/soc/amd/common/block/data_fabric/pci_segment_multi.c
new file mode 100644
index 0000000..6782221
--- /dev/null
+++ b/src/soc/amd/common/block/data_fabric/pci_segment_multi.c
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <amdblocks/data_fabric.h>
+#include <console/console.h>
+#include <device/device.h>
+#include <types.h>
+
+enum cb_err data_fabric_get_pci_bus_numbers(struct device *domain, uint8_t *first_bus,
+					    uint8_t *last_bus)
+{
+	union df_pci_cfg_base pci_bus_base;
+	union df_pci_cfg_limit pci_bus_limit;
+
+	for (unsigned int i = 0; i < DF_PCI_CFG_MAP_COUNT; i++) {
+		pci_bus_base.raw = data_fabric_broadcast_read32(DF_PCI_CFG_BASE(i));
+		pci_bus_limit.raw = data_fabric_broadcast_read32(DF_PCI_CFG_LIMIT(i));
+
+		/* TODO: Systems with more than one PCI root need to check to which PCI root
+		   the PCI bus number range gets decoded to. */
+		if (pci_bus_base.we && pci_bus_base.re) {
+			/* TODO: Implement support for multiple PCI segments in coreboot */
+			if (pci_bus_base.segment_num) {
+				printk(BIOS_ERR, "DF PCI CFG register pair %d uses bus "
+						 "segment != 0.\n", i);
+				return CB_ERR;
+			}
+
+			*first_bus = pci_bus_base.bus_num_base;
+			*last_bus = pci_bus_limit.bus_num_limit;
+			return CB_SUCCESS;
+		}
+	}
+
+	return CB_ERR;
+}
diff --git a/src/soc/amd/common/block/data_fabric/pci_segment_single.c b/src/soc/amd/common/block/data_fabric/pci_segment_single.c
new file mode 100644
index 0000000..5f2152b
--- /dev/null
+++ b/src/soc/amd/common/block/data_fabric/pci_segment_single.c
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <amdblocks/data_fabric.h>
+#include <device/device.h>
+#include <types.h>
+
+enum cb_err data_fabric_get_pci_bus_numbers(struct device *domain, uint8_t *first_bus,
+					    uint8_t *last_bus)
+{
+	union df_pci_cfg_map pci_bus_map;
+
+	for (unsigned int i = 0; i < DF_PCI_CFG_MAP_COUNT; i++) {
+		pci_bus_map.raw = data_fabric_broadcast_read32(DF_PCI_CFG_MAP(i));
+
+		/* TODO: Systems with more than one PCI root need to check to which PCI root
+		   the PCI bus number range gets decoded to. */
+		if (pci_bus_map.we && pci_bus_map.re) {
+			*first_bus = pci_bus_map.bus_num_base;
+			*last_bus = pci_bus_map.bus_num_limit;
+			return CB_SUCCESS;
+		}
+	}
+
+	return CB_ERR;
+}
diff --git a/src/soc/amd/common/block/include/amdblocks/data_fabric.h b/src/soc/amd/common/block/include/amdblocks/data_fabric.h
index f1a7c29..341bad6 100644
--- a/src/soc/amd/common/block/include/amdblocks/data_fabric.h
+++ b/src/soc/amd/common/block/include/amdblocks/data_fabric.h
@@ -48,6 +48,9 @@
 int data_fabric_find_unused_mmio_reg(void);
 void data_fabric_set_mmio_np(void);
 
+enum cb_err data_fabric_get_pci_bus_numbers(struct device *domain, uint8_t *first_bus,
+					    uint8_t *last_bus);
+
 /* Inform the resource allocator about the usable IO and MMIO regions and PCI bus numbers */
 void amd_pci_domain_read_resources(struct device *domain);
 void amd_pci_domain_scan_bus(struct device *domain);
diff --git a/src/soc/amd/glinda/Kconfig b/src/soc/amd/glinda/Kconfig
index a241ad1..b51cff3 100644
--- a/src/soc/amd/glinda/Kconfig
+++ b/src/soc/amd/glinda/Kconfig
@@ -43,6 +43,7 @@
 	select SOC_AMD_COMMON_BLOCK_CPUFREQ_FAM1AH
 	select SOC_AMD_COMMON_BLOCK_DATA_FABRIC
 	select SOC_AMD_COMMON_BLOCK_DATA_FABRIC_DOMAIN
+	select SOC_AMD_COMMON_BLOCK_DATA_FABRIC_MULTI_PCI_SEGMENT
 	select SOC_AMD_COMMON_BLOCK_ESPI_EXTENDED_DECODE_RANGES # TODO: Check if this is still correct
 	select SOC_AMD_COMMON_BLOCK_GRAPHICS		# TODO: Check if this is still correct
 	select SOC_AMD_COMMON_BLOCK_HAS_ESPI		# TODO: Check if this is still correct
diff --git a/src/soc/amd/phoenix/Kconfig b/src/soc/amd/phoenix/Kconfig
index b506464..6bed749 100644
--- a/src/soc/amd/phoenix/Kconfig
+++ b/src/soc/amd/phoenix/Kconfig
@@ -44,6 +44,7 @@
 	select SOC_AMD_COMMON_BLOCK_CPUFREQ_FAM17H_19H
 	select SOC_AMD_COMMON_BLOCK_DATA_FABRIC
 	select SOC_AMD_COMMON_BLOCK_DATA_FABRIC_DOMAIN
+	select SOC_AMD_COMMON_BLOCK_DATA_FABRIC_MULTI_PCI_SEGMENT
 	select SOC_AMD_COMMON_BLOCK_ESPI_EXTENDED_DECODE_RANGES
 	select SOC_AMD_COMMON_BLOCK_GRAPHICS		# TODO: Check if this is still correct
 	select SOC_AMD_COMMON_BLOCK_HAS_ESPI