diff --git a/src/soc/amd/common/block/data_fabric/Kconfig b/src/soc/amd/common/block/data_fabric/Kconfig index 42cd6ec070..c98bac48ee 100644 --- a/src/soc/amd/common/block/data_fabric/Kconfig +++ b/src/soc/amd/common/block/data_fabric/Kconfig @@ -3,3 +3,12 @@ config SOC_AMD_COMMON_BLOCK_DATA_FABRIC help Select this option to add data fabric configuration related functionality to the build. + +config SOC_AMD_COMMON_BLOCK_DATA_FABRIC_DOMAIN + bool + depends on SOC_AMD_COMMON_BLOCK_DATA_FABRIC + help + Select this option to add functionality to the build to tell the + 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. diff --git a/src/soc/amd/common/block/data_fabric/Makefile.inc b/src/soc/amd/common/block/data_fabric/Makefile.inc index 1aa0308938..868df82e1c 100644 --- a/src/soc/amd/common/block/data_fabric/Makefile.inc +++ b/src/soc/amd/common/block/data_fabric/Makefile.inc @@ -1,6 +1,4 @@ ## SPDX-License-Identifier: GPL-2.0-only -ifeq ($(CONFIG_SOC_AMD_COMMON_BLOCK_DATA_FABRIC),y) -ramstage-y += data_fabric_helper.c - -endif # CONFIG_SOC_AMD_COMMON_BLOCK_DATA_FABRIC +ramstage-$(CONFIG_SOC_AMD_COMMON_BLOCK_DATA_FABRIC) += data_fabric_helper.c +ramstage-$(CONFIG_SOC_AMD_COMMON_BLOCK_DATA_FABRIC_DOMAIN) += domain.c diff --git a/src/soc/amd/common/block/data_fabric/domain.c b/src/soc/amd/common/block/data_fabric/domain.c new file mode 100644 index 0000000000..9b9ccc4012 --- /dev/null +++ b/src/soc/amd/common/block/data_fabric/domain.c @@ -0,0 +1,160 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include +#include +#include +#include + +void amd_pci_domain_scan_bus(struct device *domain) +{ + 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; + + /* Set bus first number of PCI root */ + domain->link_list->secondary = bus; + /* subordinate needs to be the same as secondary before pci_domain_scan_bus call. */ + domain->link_list->subordinate = bus; + + pci_domain_scan_bus(domain); + + /* pci_domain_scan_bus will modify subordinate, so change it back to the maximum + bus number decoded to this PCI root for the acpigen_resource_producer_bus_number + call to write the correct ACPI code. */ + domain->link_list->subordinate = limit; +} + +/* Read the registers and return normalized values */ +static void data_fabric_get_mmio_base_size(unsigned int reg, + resource_t *mmio_base, resource_t *mmio_limit) +{ + const uint32_t base_reg = data_fabric_broadcast_read32(0, DF_MMIO_BASE(reg)); + const uint32_t limit_reg = data_fabric_broadcast_read32(0, DF_MMIO_LIMIT(reg)); + /* The raw register values are bits 47..16 of the actual address */ + *mmio_base = (resource_t)base_reg << D18F0_MMIO_SHIFT; + *mmio_limit = (((resource_t)limit_reg + 1) << D18F0_MMIO_SHIFT) - 1; +} + +static void print_df_mmio_outside_of_cpu_mmio_error(unsigned int reg) +{ + printk(BIOS_WARNING, "DF MMIO register %u outside of CPU MMIO region.\n", reg); +} + +static bool is_mmio_region_valid(unsigned int reg, resource_t mmio_base, resource_t mmio_limit) +{ + if (mmio_base > mmio_limit) { + printk(BIOS_WARNING, "DF MMIO register %u's base is above its limit.\n", reg); + return false; + } + if (mmio_base >= 4ULL * GiB) { + /* MMIO region above 4GB needs to be above TOP_MEM2 MSR value */ + if (mmio_base < get_top_of_mem_above_4gb()) { + print_df_mmio_outside_of_cpu_mmio_error(reg); + return false; + } + } else { + /* MMIO region below 4GB needs to be above TOP_MEM MSR value */ + if (mmio_base < get_top_of_mem_below_4gb()) { + print_df_mmio_outside_of_cpu_mmio_error(reg); + return false; + } + /* MMIO region below 4GB mustn't cross the 4GB boundary. */ + if (mmio_limit >= 4ULL * GiB) { + printk(BIOS_WARNING, "DF MMIO register %u crosses 4GB boundary.\n", + reg); + return false; + } + } + + return true; +} + +static void report_data_fabric_mmio(struct device *domain, unsigned int idx, + resource_t mmio_base, resource_t mmio_limit) +{ + struct resource *res; + res = new_resource(domain, idx); + res->base = mmio_base; + res->limit = mmio_limit; + res->flags = IORESOURCE_MEM | IORESOURCE_ASSIGNED; +} + +/* Tell the resource allocator about the usable MMIO ranges configured in the data fabric */ +static void add_data_fabric_mmio_regions(struct device *domain, unsigned int *idx) +{ + union df_mmio_control ctrl; + resource_t mmio_base; + resource_t mmio_limit; + + /* The last 12GB of the usable address space are reserved and can't be used for MMIO */ + const resource_t reserved_upper_mmio_base = + (1ULL << get_usable_physical_address_bits()) - DF_RESERVED_TOP_12GB_MMIO_SIZE; + + for (unsigned int i = 0; i < DF_MMIO_REG_SET_COUNT; i++) { + ctrl.raw = data_fabric_broadcast_read32(0, DF_MMIO_CONTROL(i)); + + /* Relevant MMIO regions need to have both reads and writes enabled */ + if (!ctrl.we || !ctrl.re) + continue; + + /* Non-posted region contains fixed FCH MMIO devices */ + if (ctrl.np) + continue; + + /* TODO: Systems with more than one PCI root need to check to which PCI root + the MMIO range gets decoded to. */ + + data_fabric_get_mmio_base_size(i, &mmio_base, &mmio_limit); + + if (!is_mmio_region_valid(i, mmio_base, mmio_limit)) + continue; + + /* Make sure to not report a region overlapping with the fixed MMIO resources + below 4GB or the reserved MMIO range in the last 12GB of the addressable + address range. The code assumes that the fixed MMIO resources below 4GB + are between IO_APIC_ADDR and the 4GB boundary. */ + if (mmio_base < 4ULL * GiB) { + if (mmio_base >= IO_APIC_ADDR) + continue; + if (mmio_limit >= IO_APIC_ADDR) + mmio_limit = IO_APIC_ADDR - 1; + } else { + if (mmio_base >= reserved_upper_mmio_base) + continue; + if (mmio_limit >= reserved_upper_mmio_base) + mmio_limit = reserved_upper_mmio_base - 1; + } + + report_data_fabric_mmio(domain, (*idx)++, mmio_base, mmio_limit); + } +} + +/* Tell the resource allocator about the usable I/O space */ +static void add_io_regions(struct device *domain, unsigned int *idx) +{ + struct resource *res; + + /* TODO: Systems with more than one PCI root need to read the data fabric registers to + see which IO ranges get decoded to which PCI root. */ + + res = new_resource(domain, (*idx)++); + res->base = 0; + res->limit = 0xffff; + res->flags = IORESOURCE_IO | IORESOURCE_ASSIGNED; +} + +void amd_pci_domain_read_resources(struct device *domain) +{ + unsigned int idx = 0; + + add_io_regions(domain, &idx); + + add_data_fabric_mmio_regions(domain, &idx); +} 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 c224a11c1e..c021f50c2c 100644 --- a/src/soc/amd/common/block/include/amdblocks/data_fabric.h +++ b/src/soc/amd/common/block/include/amdblocks/data_fabric.h @@ -18,6 +18,9 @@ #define DF_MMIO_LIMIT(reg) (D18F0_MMIO_LIMIT0 + DF_MMIO_REG_OFFSET(reg)) #define DF_MMIO_CONTROL(reg) (D18F0_MMIO_CTRL0 + DF_MMIO_REG_OFFSET(reg)) +/* Last 12GB of the usable address space are reserved */ +#define DF_RESERVED_TOP_12GB_MMIO_SIZE (12ULL * GiB) + uint32_t data_fabric_read32(uint8_t function, uint16_t reg, uint8_t instance_id); void data_fabric_write32(uint8_t function, uint16_t reg, uint8_t instance_id, uint32_t data); @@ -40,4 +43,8 @@ void data_fabric_disable_mmio_reg(unsigned int reg); int data_fabric_find_unused_mmio_reg(void); void data_fabric_set_mmio_np(void); +/* 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); + #endif /* AMD_BLOCK_DATA_FABRIC_H */