soc/intel/xeon_sp/uncore.c: Add CXL memory into memory map

If the host supports CXL, get proximity domain info from FSP HOB. The
proximity domains may include both processor domains and CXL domains.

Add header definition for proximity domain.

Add CXL memory into memory map.

Change-Id: If3f856958a3e6ed3909240ee455bb639e487087f
Signed-off-by: Jonathan Zhang <jonzhang@meta.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/72617
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/include/soc/numa.h b/src/soc/intel/xeon_sp/include/soc/numa.h
new file mode 100644
index 0000000..48141c7
--- /dev/null
+++ b/src/soc/intel/xeon_sp/include/soc/numa.h
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+/*
+ * This header file defines data structures and operations related
+ * to NUMA proximity domains.
+ */
+#ifndef NUMA_H
+#define NUMA_H
+
+#include <types.h>
+
+enum proximity_domain_type {
+	PD_TYPE_PROCESSOR,
+	/*
+	 * The Generic Initiator concept is used in ACPI spec. A typical
+	 * Generic Initiator domain is a CXL memory device.
+	 */
+	PD_TYPE_GENERIC_INITIATOR,
+};
+
+/*
+ * This proximity domain structure records all data related to
+ * a proximity doamin needed for following purpose:
+ * a. Device resource allocation. IIO stack involving CXL device
+ *    needs to have different resource allocation method.
+ * b. e820 table setup. For example, CXL memory region may need to
+ *    be set as soft reserved, since it is specific purpose memory.
+ * c. ACPI NUMA tables (SRAT, SLIT, HMAT).
+ */
+struct proximity_domain {
+	enum proximity_domain_type pd_type;
+	/*
+	 * For processor domain, this holds the socket #.
+	 * For generic initiator domain, this indicates to which socket the
+	 * device is attached to. CXL 2.0 allows interleaving within and between
+	 * sockets, so we need a bitmap.
+	 */
+	uint8_t socket_bitmap;
+	/* Relative distances (memory latency) from all domains */
+	uint8_t *distances;
+	/*
+	 * Below fields are set to 0 for processor domains.
+	 */
+	uint32_t device_handle; /* This holds PCIe device segment, BDF info */
+	uint32_t base; /* Memory region base address in the unit of 64MB */
+	uint32_t size; /* Memory region size in the unit of 64MB */
+};
+
+struct proximity_domains {
+	uint8_t num_pds;
+	struct proximity_domain *pds;
+};
+
+extern struct proximity_domains pds;
+
+void dump_pds(void);
+enum cb_err fill_pds(void);
+
+/*
+ * Return the total size of memory regions in generic initiator affinity
+ * domains.  The size is in unit of 64MB.
+ */
+uint32_t get_generic_initiator_mem_size(void);
+
+#endif /* NUMA_H */
diff --git a/src/soc/intel/xeon_sp/uncore.c b/src/soc/intel/xeon_sp/uncore.c
index 418736e..5249a94 100644
--- a/src/soc/intel/xeon_sp/uncore.c
+++ b/src/soc/intel/xeon_sp/uncore.c
@@ -5,6 +5,7 @@
 #include <cpu/x86/lapic_def.h>
 #include <device/pci.h>
 #include <device/pci_ids.h>
+#include <drivers/ocp/include/vpd.h>
 #include <soc/acpi.h>
 #include <soc/iomap.h>
 #include <soc/pci_devs.h>
@@ -13,8 +14,15 @@
 #include <fsp/util.h>
 #include <security/intel/txt/txt_platform.h>
 #include <security/intel/txt/txt.h>
+#include <soc/numa.h>
+#include <soc/soc_util.h>
 #include <stdint.h>
 
+struct proximity_domains pds = {
+	.num_pds = 0,
+	.pds = NULL,
+};
+
 struct map_entry {
 	uint32_t    reg;
 	int         is_64_bit;
@@ -238,9 +246,42 @@
 				   mc_values[TOLM_REG]);
 	LOG_RESOURCE("mmio_tolm", dev, res);
 
-	/* 4GiB -> TOHM */
-	res = upper_ram_end(dev, index++, mc_values[TOHM_REG] + 1);
-	LOG_RESOURCE("high_ram", dev, res);
+	if (CONFIG(SOC_INTEL_HAS_CXL)) {
+		/* 4GiB -> CXL Memory */
+		uint32_t gi_mem_size;
+		gi_mem_size = get_generic_initiator_mem_size();
+
+		res = reserved_ram_from_to(dev, index++, 0x100000000,
+				   mc_values[TOHM_REG] - (uint64_t)gi_mem_size + 1);
+		LOG_RESOURCE("high_ram", dev, res);
+
+		/* CXL Memory */
+		uint8_t i;
+		for (i = 0; i < pds.num_pds; i++) {
+			if (pds.pds[i].pd_type == PD_TYPE_PROCESSOR)
+				continue;
+
+			if (CONFIG(OCP_VPD)) {
+				unsigned long flags = IORESOURCE_CACHEABLE;
+				int cxl_mode = get_cxl_mode_from_vpd();
+				if (cxl_mode == CXL_SPM)
+					flags |= IORESOURCE_SOFT_RESERVE;
+				else
+					flags |= IORESOURCE_STORED;
+
+				res = fixed_mem_range_flags(dev, index++, (uint64_t)pds.pds[i].base,
+					(uint64_t)pds.pds[i].size, flags);
+				if (cxl_mode == CXL_SPM)
+					LOG_RESOURCE("specific_purpose_memory", dev, res);
+				else
+					LOG_RESOURCE("CXL_memory", dev, res);
+			}
+		}
+	} else {
+		/* 4GiB -> TOHM */
+		res = upper_ram_end(dev, index++, mc_values[TOHM_REG] + 1);
+		LOG_RESOURCE("high_ram", dev, res);
+	}
 
 	/* add MMIO CFG resource */
 	res = mmio_from_to(dev, index++, mc_values[MMCFG_BASE_REG],
@@ -271,6 +312,14 @@
 {
 	int index = 0;
 
+	if (CONFIG(SOC_INTEL_HAS_CXL)) {
+		/* Construct NUMA data structure. This is needed for CXL. */
+		if (fill_pds() != CB_SUCCESS)
+			pds.num_pds = 0;
+
+		dump_pds();
+	}
+
 	/* Read standard PCI resources. */
 	pci_dev_read_resources(dev);