soc/intel/xeon_sp: Attach UBOX stacks

Attach UBOX stacks on newer generation Xeon-SP.
In order to use PCI drivers for UBOX devices, locating UBOX devices
by vendor and device IDs and replacing device access by specifying
S:B:D:F numbers, add a PCI domain for the UBOX stacks and let the
PCI enumerator index all devices.

Since there are no PCI BARs on the UBOX bus the PCI locator doesn't
have to assign resources on those buses.

Once all PCI devices on the UBOX stack can be located without knowing
their UBOX bus number and PCI segment the Xeon-SP code can fully
enable the multi PCI segment group support.

Test: ibm/sbp1 (4S) is able to find all PCU devices by PCI ID.

Change-Id: I8f9d52dd117364a42de1c73d39cc86dafeaf2678
Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/80091
Reviewed-by: Arthur Heymans <arthur@aheymans.xyz>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Lean Sheng Tan <sheng.tan@9elements.com>
diff --git a/src/soc/intel/xeon_sp/chip_common.c b/src/soc/intel/xeon_sp/chip_common.c
index 144dba1..08d723b 100644
--- a/src/soc/intel/xeon_sp/chip_common.c
+++ b/src/soc/intel/xeon_sp/chip_common.c
@@ -92,7 +92,51 @@
 	.scan_bus = iio_pci_domain_scan_bus,
 };
 
-/* Attach IIO stack as domains */
+/*
+ * Used by UBOX stacks. Those contain multiple PCI host bridges, each having
+ * only one bus with UBOX devices. UBOX devices have no resources.
+ */
+static struct device_operations ubox_pcie_domain_ops = {
+	.read_resources = noop_read_resources,
+	.set_resources = noop_set_resources,
+	.scan_bus = pci_host_bridge_scan_bus,
+};
+
+/*
+ * On the first Xeon-SP generations there are no separate UBOX stacks,
+ * and the UBOX devices reside on the first and second IIO. Starting
+ * with 3rd gen Xeon-SP the UBOX devices are located on their own IIO.
+ */
+static void soc_create_ubox_domains(const union xeon_domain_path dp, struct bus *upstream,
+				    const unsigned int bus_base, const unsigned int bus_limit)
+{
+	union xeon_domain_path new_path = {
+		.domain_path = dp.domain_path
+	};
+
+	for (int i = bus_base; i <= bus_limit; i++) {
+		new_path.bus = i;
+
+		struct device_path path = {
+			.type = DEVICE_PATH_DOMAIN,
+			.domain = {
+				.domain = new_path.domain_path,
+			},
+		};
+		struct device *const domain = alloc_dev(upstream, &path);
+		if (!domain)
+			die("%s: out of memory.\n", __func__);
+
+		domain->ops = &ubox_pcie_domain_ops;
+
+		struct bus *const bus = alloc_bus(domain);
+		bus->secondary = i;
+		bus->subordinate = bus->secondary;
+		bus->max_subordinate = bus->secondary;
+	}
+}
+
+/* Attach stack as domains */
 void attach_iio_stacks(struct device *dev)
 {
 	const IIO_UDS *hob = get_iio_uds();
@@ -114,19 +158,19 @@
 			dn.stack = x;
 			dn.bus = ri->BusBase;
 
-			if (!is_pcie_iio_stack_res(ri)) {
-				if (CONFIG(HAVE_IOAT_DOMAINS))
-					soc_create_ioat_domains(dn, dev->upstream, ri);
-				continue;
-			}
+			if (is_ubox_stack_res(ri)) {
+				soc_create_ubox_domains(dn, dev->upstream, ri->BusBase, ri->BusLimit);
+			} else if (is_pcie_iio_stack_res(ri)) {
+				struct device_path path;
+				path.type = DEVICE_PATH_DOMAIN;
+				path.domain.domain = dn.domain_path;
+				struct device *iio_domain = alloc_dev(dev->upstream, &path);
+				if (iio_domain == NULL)
+					die("%s: out of memory.\n", __func__);
 
-			struct device_path path;
-			path.type = DEVICE_PATH_DOMAIN;
-			path.domain.domain = dn.domain_path;
-			struct device *iio_domain = alloc_dev(dev->upstream, &path);
-			if (iio_domain == NULL)
-				die("%s: out of memory.\n", __func__);
-			iio_domain->ops = &iio_pcie_domain_ops;
+				iio_domain->ops = &iio_pcie_domain_ops;
+			} else if (CONFIG(HAVE_IOAT_DOMAINS))
+				soc_create_ioat_domains(dn, dev->upstream, ri);
 		}
 	}
 }
diff --git a/src/soc/intel/xeon_sp/cpx/soc_util.c b/src/soc/intel/xeon_sp/cpx/soc_util.c
index 7a7e295..836cd0c 100644
--- a/src/soc/intel/xeon_sp/cpx/soc_util.c
+++ b/src/soc/intel/xeon_sp/cpx/soc_util.c
@@ -30,6 +30,11 @@
 	return res->Personality == TYPE_UBOX_IIO;
 }
 
+bool is_ubox_stack_res(const STACK_RES *res)
+{
+	return res->Personality == TYPE_UBOX;
+}
+
 uint8_t get_stack_busno(const uint8_t stack)
 {
 	if (stack >= MAX_IIO_STACK) {
diff --git a/src/soc/intel/xeon_sp/include/soc/util.h b/src/soc/intel/xeon_sp/include/soc/util.h
index fb9b138..89cc501 100644
--- a/src/soc/intel/xeon_sp/include/soc/util.h
+++ b/src/soc/intel/xeon_sp/include/soc/util.h
@@ -24,6 +24,7 @@
 
 void get_iiostack_info(struct iiostack_resource *info);
 bool is_pcie_iio_stack_res(const STACK_RES *res);
+bool is_ubox_stack_res(const STACK_RES *res);
 void bios_done_msr(void *unused);
 
 #endif
diff --git a/src/soc/intel/xeon_sp/skx/soc_util.c b/src/soc/intel/xeon_sp/skx/soc_util.c
index a5db8fd..3e02999 100644
--- a/src/soc/intel/xeon_sp/skx/soc_util.c
+++ b/src/soc/intel/xeon_sp/skx/soc_util.c
@@ -59,6 +59,29 @@
 	return res->BusBase < res->BusLimit;
 }
 
+bool is_ubox_stack_res(const STACK_RES *res)
+{
+	/*
+	 * Unlike on later platforms there's no separate "UBOX" stack.
+	 *
+	 * The UBOX devices can always be found on the first bus on the stack IIO0 (CSTACK).
+	 * This bus is also referred to as uncore bus 0 or B(30).
+	 * It has at a fixed address the UBOX:
+	 * B(30):8.0 8086:2014
+	 * B(30):8.1 8086:2015
+	 * B(30):8.2 8086:2016
+	 *
+	 * The PCU devices can always be on the first bus of the stack IIO1 (PSTACK).
+	 * This bus is also referred to as uncore bus 1 or B(31).
+	 * It has at a fixed address the PCU:
+	 * B(31):30.0 8086:2080
+	 * B(31):30.1 8086:2081
+	 * B(31):30.2 8086:2082
+	 */
+
+	return false;
+}
+
 uint8_t get_stack_busno(const uint8_t stack)
 {
 	if (stack >= MAX_IIO_STACK) {
diff --git a/src/soc/intel/xeon_sp/spr/soc_util.c b/src/soc/intel/xeon_sp/spr/soc_util.c
index 9c1a691..fb87611 100644
--- a/src/soc/intel/xeon_sp/spr/soc_util.c
+++ b/src/soc/intel/xeon_sp/spr/soc_util.c
@@ -73,6 +73,11 @@
 	return res->Personality == TYPE_UBOX_IIO;
 }
 
+bool is_ubox_stack_res(const STACK_RES *res)
+{
+	return res->Personality == TYPE_UBOX;
+}
+
 /*
  * Given a stack resource, figure out whether the corresponding stack has
  * CXL device.