soc/amd/common/data_fabric/domain: provide amd_pci_domain_fill_ssdt

Generate the PCI0 _CRS ACPI resource template to tell the OS which PCI
bus numbers and IO and MMIO regions can be used for PCI devices below
_SB/PCI0. This data corresponds to what amd_pci_domain_scan_bus and
amd_pci_domain_read_resources provided to the resource allocator. This
makes sure that the PCI0 _CRS ACPI resource template matches the
constraints the resource allocator used when allocating resources.

TEST=With also the rest of the current patch train applied, the
generated _CRS resource template contains the expected PCI bus numbers
and IO and MMIO resources and both Linux and Windows boot on Mandolin.

Signed-off-by: Arthur Heymans <arthur@aheymans.xyz>
Signed-off-by: Felix Held <felix-coreboot@felixheld.de>
Change-Id: Iaf6d38a8ef5bb0163c4d1c021bf892c323d9a448
Reviewed-on: https://review.coreboot.org/c/coreboot/+/74843
Reviewed-by: Eric Lai <eric_lai@quanta.corp-partner.google.com>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Raul Rangel <rrangel@chromium.org>
Reviewed-by: Nico Huber <nico.h@gmx.de>
diff --git a/src/soc/amd/common/block/data_fabric/domain.c b/src/soc/amd/common/block/data_fabric/domain.c
index 9b9ccc4..aec1596 100644
--- a/src/soc/amd/common/block/data_fabric/domain.c
+++ b/src/soc/amd/common/block/data_fabric/domain.c
@@ -1,8 +1,10 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 
+#include <acpi/acpigen.h>
 #include <amdblocks/cpu.h>
 #include <amdblocks/data_fabric.h>
 #include <arch/ioapic.h>
+#include <arch/vga.h>
 #include <console/console.h>
 #include <cpu/amd/mtrr.h>
 #include <device/device.h>
@@ -158,3 +160,89 @@
 
 	add_data_fabric_mmio_regions(domain, &idx);
 }
+
+static void write_ssdt_domain_io_producer_range_helper(const char *domain_name,
+						       resource_t base, resource_t limit)
+{
+	printk(BIOS_DEBUG, "%s _CRS: adding IO range [%llx-%llx]\n", domain_name, base, limit);
+	acpigen_resource_producer_io(base, limit);
+}
+
+static void write_ssdt_domain_io_producer_range(const char *domain_name,
+						resource_t base, resource_t limit)
+{
+	/*
+	 * Split the IO region at the PCI config IO ports so that the IO resource producer
+	 * won't cover the same IO ports that the IO resource consumer for the PCI config IO
+	 * ports in the same ACPI device already covers.
+	 */
+	if (base < PCI_IO_CONFIG_INDEX) {
+		write_ssdt_domain_io_producer_range_helper(domain_name,
+					base,
+					MIN(limit, PCI_IO_CONFIG_INDEX - 1));
+	}
+	if (limit > PCI_IO_CONFIG_LAST_PORT) {
+		write_ssdt_domain_io_producer_range_helper(domain_name,
+					MAX(base, PCI_IO_CONFIG_LAST_PORT + 1),
+					limit);
+	}
+}
+
+static void write_ssdt_domain_mmio_producer_range(const char *domain_name,
+						  resource_t base, resource_t limit)
+{
+	printk(BIOS_DEBUG, "%s _CRS: adding MMIO range [%llx-%llx]\n",
+	       domain_name, base, limit);
+	acpigen_resource_producer_mmio(base, limit,
+		MEM_RSRC_FLAG_MEM_READ_WRITE | MEM_RSRC_FLAG_MEM_ATTR_NON_CACHE);
+}
+
+void amd_pci_domain_fill_ssdt(const struct device *domain)
+{
+	const char *acpi_scope = acpi_device_path(domain);
+	printk(BIOS_DEBUG, "%s ACPI scope: '%s'\n", __func__, acpi_scope);
+	acpigen_write_scope(acpi_device_path(domain));
+
+	acpigen_write_name("_CRS");
+	acpigen_write_resourcetemplate_header();
+
+	/* PCI bus number range in domain */
+	printk(BIOS_DEBUG, "%s _CRS: adding busses [%x-%x]\n", acpi_device_name(domain),
+	       domain->link_list->secondary, domain->link_list->subordinate);
+	acpigen_resource_producer_bus_number(domain->link_list->secondary,
+					     domain->link_list->subordinate);
+
+	if (domain->link_list->secondary == 0) {
+		/* ACPI 6.4.2.5 I/O Port Descriptor */
+		acpigen_write_io16(PCI_IO_CONFIG_INDEX, PCI_IO_CONFIG_LAST_PORT, 1,
+				   PCI_IO_CONFIG_PORT_COUNT, 1);
+	}
+
+	struct resource *res;
+	for (res = domain->resource_list; res != NULL; res = res->next) {
+		if (!(res->flags & IORESOURCE_ASSIGNED))
+			return;
+		switch (res->flags & IORESOURCE_TYPE_MASK) {
+		case IORESOURCE_IO:
+			write_ssdt_domain_io_producer_range(acpi_device_name(domain),
+							    res->base, res->limit);
+			break;
+		case IORESOURCE_MEM:
+			write_ssdt_domain_mmio_producer_range(acpi_device_name(domain),
+							      res->base, res->limit);
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (domain->link_list->bridge_ctrl & PCI_BRIDGE_CTL_VGA) {
+		printk(BIOS_DEBUG, "%s _CRS: adding VGA resource\n", acpi_device_name(domain));
+		acpigen_resource_producer_mmio(VGA_MMIO_BASE, VGA_MMIO_LIMIT,
+			MEM_RSRC_FLAG_MEM_READ_WRITE | MEM_RSRC_FLAG_MEM_ATTR_CACHE);
+	}
+
+	acpigen_write_resourcetemplate_footer();
+	/* Scope */
+	acpigen_pop_len();
+}
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 c021f50..0ed3272 100644
--- a/src/soc/amd/common/block/include/amdblocks/data_fabric.h
+++ b/src/soc/amd/common/block/include/amdblocks/data_fabric.h
@@ -47,4 +47,6 @@
 void amd_pci_domain_read_resources(struct device *domain);
 void amd_pci_domain_scan_bus(struct device *domain);
 
+void amd_pci_domain_fill_ssdt(const struct device *domain);
+
 #endif /* AMD_BLOCK_DATA_FABRIC_H */