diff options
Diffstat (limited to 'src/northbridge/amd')
-rw-r--r-- | src/northbridge/amd/pi/00730F01/northbridge.c | 390 |
1 files changed, 239 insertions, 151 deletions
diff --git a/src/northbridge/amd/pi/00730F01/northbridge.c b/src/northbridge/amd/pi/00730F01/northbridge.c index d324689955..4a24d3550a 100644 --- a/src/northbridge/amd/pi/00730F01/northbridge.c +++ b/src/northbridge/amd/pi/00730F01/northbridge.c @@ -1,9 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* This file is part of the coreboot project. */ +#include <commonlib/helpers.h> #include <console/console.h> #include <device/pci_ops.h> #include <arch/acpi.h> +#include <arch/acpi_ivrs.h> #include <arch/ioapic.h> #include <stdint.h> #include <device/device.h> @@ -11,6 +13,7 @@ #include <device/pci_ids.h> #include <device/hypertransport.h> #include <string.h> +#include <stdlib.h> #include <lib.h> #include <cpu/cpu.h> #include <Porting.h> @@ -22,6 +25,7 @@ #include <arch/acpigen.h> #include <northbridge/amd/pi/nb_common.h> #include <northbridge/amd/agesa/agesa_helper.h> +#include <southbridge/amd/pi/hudson/pci_devs.h> #define MAX_NODE_NUMS MAX_NODES #define PCIE_CAP_AER BIT(5) @@ -428,59 +432,139 @@ static unsigned long acpi_fill_hest(acpi_hest_t *hest) return (unsigned long)current; } -static void add_ivhd_dev_entry(struct device *parent, struct device *dev, - unsigned long *current, uint16_t *length, - uint8_t type, uint8_t data) +unsigned long acpi_fill_ivrs_ioapic(acpi_ivrs_t *ivrs, unsigned long current) +{ + /* 8-byte IVHD structures must be aligned to the 8-byte boundary. */ + current = ALIGN_UP(current, 8); + ivrs_ivhd_special_t *ivhd_ioapic = (ivrs_ivhd_special_t *)current; + + ivhd_ioapic->type = IVHD_DEV_8_BYTE_EXT_SPECIAL_DEV; + ivhd_ioapic->reserved = 0x0000; + ivhd_ioapic->dte_setting = IVHD_DTE_LINT_1_PASS | IVHD_DTE_LINT_0_PASS | + IVHD_DTE_SYS_MGT_NO_TRANS | IVHD_DTE_NMI_PASS | + IVHD_DTE_EXT_INT_PASS | IVHD_DTE_INIT_PASS; + ivhd_ioapic->handle = CONFIG_MAX_CPUS; /* FCH IOAPIC ID */ + ivhd_ioapic->source_dev_id = PCI_DEVFN(SMBUS_DEV, SMBUS_FUNC); + ivhd_ioapic->variety = IVHD_SPECIAL_DEV_IOAPIC; + current += sizeof(ivrs_ivhd_special_t); + + ivhd_ioapic = (ivrs_ivhd_special_t *)current; + + ivhd_ioapic->type = IVHD_DEV_8_BYTE_EXT_SPECIAL_DEV; + ivhd_ioapic->reserved = 0x0000; + ivhd_ioapic->dte_setting = 0x00; + ivhd_ioapic->handle = CONFIG_MAX_CPUS + 1; /* GNB IOAPIC ID */ + ivhd_ioapic->source_dev_id = PCI_DEVFN(0, 1); + ivhd_ioapic->variety = IVHD_SPECIAL_DEV_IOAPIC; + current += sizeof(ivrs_ivhd_special_t); + + return current; +} + +static unsigned long ivhd_describe_hpet(unsigned long current) +{ + /* 8-byte IVHD structures must be aligned to the 8-byte boundary. */ + current = ALIGN_UP(current, 8); + ivrs_ivhd_special_t *ivhd_hpet = (ivrs_ivhd_special_t *)current; + + ivhd_hpet->type = IVHD_DEV_8_BYTE_EXT_SPECIAL_DEV; + ivhd_hpet->reserved = 0x0000; + ivhd_hpet->dte_setting = 0x00; + ivhd_hpet->handle = 0x00; + ivhd_hpet->source_dev_id = PCI_DEVFN(SMBUS_DEV, SMBUS_FUNC); + ivhd_hpet->variety = IVHD_SPECIAL_DEV_HPET; + current += sizeof(ivrs_ivhd_special_t); + + return current; +} + +static unsigned long ivhd_dev_range(unsigned long current, uint16_t start_devid, + uint16_t end_devid, uint8_t setting) +{ + /* 4-byte IVHD structures must be aligned to the 4-byte boundary. */ + current = ALIGN_UP(current, 4); + ivrs_ivhd_generic_t *ivhd_range = (ivrs_ivhd_generic_t *)current; + + /* Create the start range IVHD entry */ + ivhd_range->type = IVHD_DEV_4_BYTE_START_RANGE; + ivhd_range->dev_id = start_devid; + ivhd_range->dte_setting = setting; + current += sizeof(ivrs_ivhd_generic_t); + + /* Create the end range IVHD entry */ + ivhd_range = (ivrs_ivhd_generic_t *)current; + ivhd_range->type = IVHD_DEV_4_BYTE_END_RANGE; + ivhd_range->dev_id = end_devid; + ivhd_range->dte_setting = setting; + current += sizeof(ivrs_ivhd_generic_t); + + return current; +} + +static unsigned long add_ivhd_dev_entry(struct device *parent, struct device *dev, + unsigned long *current, uint8_t type, uint8_t data) { - uint8_t *p; - p = (uint8_t *) *current; - - if (type == 0x2) { - /* Entry type */ - p[0] = type; - /* Device */ - p[1] = dev->path.pci.devfn; - /* Bus */ - p[2] = dev->bus->secondary; - /* Data */ - p[3] = data; - /* [4:7] Padding */ - p[4] = 0x0; - p[5] = 0x0; - p[6] = 0x0; - p[7] = 0x0; - *length += 8; - *current += 8; - } else if (type == 0x42) { - /* Entry type */ - p[0] = type; - /* Device */ - p[1] = dev->path.pci.devfn; - /* Bus */ - p[2] = dev->bus->secondary; - /* Data */ - p[3] = 0x0; - /* Reserved */ - p[4] = 0x0; - /* Device */ - p[5] = parent->path.pci.devfn; - /* Bus */ - p[6] = parent->bus->secondary; - /* Reserved */ - p[7] = 0x0; - *length += 8; - *current += 8; + if (type == IVHD_DEV_4_BYTE_SELECT) { + /* 4-byte IVHD structures must be aligned to the 4-byte boundary. */ + *current = ALIGN_UP(*current, 4); + ivrs_ivhd_generic_t *ivhd_entry = (ivrs_ivhd_generic_t *)*current; + + ivhd_entry->type = type; + ivhd_entry->dev_id = dev->path.pci.devfn | (dev->bus->secondary << 8); + ivhd_entry->dte_setting = data; + *current += sizeof(ivrs_ivhd_generic_t); + } else if (type == IVHD_DEV_8_BYTE_ALIAS_SELECT) { + /* 8-byte IVHD structures must be aligned to the 8-byte boundary. */ + *current = ALIGN_UP(*current, 8); + ivrs_ivhd_alias_t *ivhd_entry = (ivrs_ivhd_alias_t *)*current; + + ivhd_entry->type = type; + ivhd_entry->dev_id = dev->path.pci.devfn | (dev->bus->secondary << 8); + ivhd_entry->dte_setting = data; + ivhd_entry->reserved1 = 0; + ivhd_entry->reserved2 = 0; + ivhd_entry->source_dev_id = parent->path.pci.devfn | + (parent->bus->secondary << 8); + *current += sizeof(ivrs_ivhd_alias_t); } + + return *current; } -static void add_ivrs_device_entries(struct device *parent, struct device *dev, +static void ivrs_add_device_or_bridge(struct device *parent, struct device *dev, + unsigned long *current, uint16_t *ivhd_length) +{ + unsigned int header_type, is_pcie; + unsigned long current_backup; + + header_type = dev->hdr_type & 0x7f; + is_pcie = pci_find_capability(dev, PCI_CAP_ID_PCIE); + + if (((header_type == PCI_HEADER_TYPE_NORMAL) || + (header_type == PCI_HEADER_TYPE_BRIDGE)) && is_pcie) { + /* Device or Bridge is PCIe */ + current_backup = *current; + add_ivhd_dev_entry(parent, dev, current, IVHD_DEV_4_BYTE_SELECT, 0x0); + *ivhd_length += (*current - current_backup); + } else if ((header_type == PCI_HEADER_TYPE_NORMAL) && !is_pcie) { + /* Device is legacy PCI or PCI-X */ + current_backup = *current; + add_ivhd_dev_entry(parent, dev, current, IVHD_DEV_8_BYTE_ALIAS_SELECT, 0x0); + *ivhd_length += (*current - current_backup); + } +} + +static void add_ivhd_device_entries(struct device *parent, struct device *dev, unsigned int depth, int linknum, int8_t *root_level, - unsigned long *current, uint16_t *length) + unsigned long *current, uint16_t *ivhd_length) { struct device *sibling; struct bus *link; - unsigned int header_type; - unsigned int is_pcie; + + if (!root_level) { + root_level = malloc(sizeof(int8_t)); + *root_level = -1; + } if (dev->path.type == DEVICE_PATH_PCI) { @@ -489,154 +573,158 @@ static void add_ivrs_device_entries(struct device *parent, struct device *dev, *root_level = depth; if ((*root_level != -1) && (dev->enabled)) { - if (depth == *root_level) { - if (dev->path.pci.devfn == (0x14 << 3)) { - /* SMBUS controller */ - add_ivhd_dev_entry(parent, dev, current, length, 0x2, 0x97); - } else if (dev->path.pci.devfn != 0x2 && - dev->path.pci.devfn < (0x2 << 3)) { - /* FCH control device */ - } else { - /* Other devices */ - add_ivhd_dev_entry(parent, dev, current, length, 0x2, 0x0); - } - } else { - header_type = dev->hdr_type & 0x7f; - is_pcie = pci_find_capability(dev, PCI_CAP_ID_PCIE); - if (((header_type == PCI_HEADER_TYPE_NORMAL) || - (header_type == PCI_HEADER_TYPE_BRIDGE)) - && is_pcie) { - /* Device or Bridge is PCIe */ - add_ivhd_dev_entry(parent, dev, current, length, 0x2, 0x0); - } else if ((header_type == PCI_HEADER_TYPE_NORMAL) && - !is_pcie) { - add_ivhd_dev_entry(parent, dev, current, length, 0x42, 0x0); - /* Device is legacy PCI or PCI-X */ - } - } + if (depth != *root_level) + ivrs_add_device_or_bridge(parent, dev, current, ivhd_length); } } for (link = dev->link_list; link; link = link->next) for (sibling = link->children; sibling; sibling = sibling->sibling) - add_ivrs_device_entries(dev, sibling, depth + 1, depth, - root_level, current, length); + add_ivhd_device_entries(dev, sibling, depth + 1, depth, root_level, + current, ivhd_length); + + free(root_level); } -unsigned long acpi_fill_ivrs_ioapic(acpi_ivrs_t *ivrs, unsigned long current) +#define IOMMU_MMIO32(x) (*((volatile uint32_t *)(x))) +#define EFR_SUPPORT BIT(27) + +static unsigned long acpi_fill_ivrs11(unsigned long current, acpi_ivrs_t *ivrs_agesa) { - uint8_t *p; - - uint32_t apicid_sb800; - uint32_t apicid_northbridge; - - apicid_sb800 = CONFIG_MAX_CPUS; - apicid_northbridge = CONFIG_MAX_CPUS + 1; - - /* Describe NB IOAPIC */ - p = (uint8_t *)current; - p[0] = 0x48; /* Entry type */ - p[1] = 0; /* Device */ - p[2] = 0; /* Bus */ - p[3] = 0x0; /* Data */ - p[4] = apicid_northbridge; /* IOAPIC ID */ - p[5] = 0x0; /* Device 0 Function 0 */ - p[6] = 0x0; /* Northbridge bus */ - p[7] = 0x1; /* Variety */ - current += 8; - - /* Describe SB IOAPIC */ - p = (uint8_t *)current; - p[0] = 0x48; /* Entry type */ - p[1] = 0; /* Device */ - p[2] = 0; /* Bus */ - p[3] = 0xd7; /* Data */ - p[4] = apicid_sb800; /* IOAPIC ID */ - p[5] = 0x14 << 3; /* Device 0x14 Function 0 */ - p[6] = 0x0; /* Southbridge bus */ - p[7] = 0x1; /* Variety */ - current += 8; + acpi_ivrs_ivhd11_t *ivhd_11; + unsigned long current_backup; + + /* + * These devices should be already found by previous function. + * Do not perform NULL checks. + */ + struct device *nb_dev = pcidev_on_root(0, 0); + struct device *iommu_dev = pcidev_on_root(0, 2); + + /* + * In order to utilize all features, firmware should expose type 11h + * IVHD which supersedes the type 10h. + */ + memset((void *)current, 0, sizeof(acpi_ivrs_ivhd11_t)); + ivhd_11 = (acpi_ivrs_ivhd11_t *)current; + + /* Enable EFR */ + ivhd_11->type = IVHD_BLOCK_TYPE_FULL__FIXED; + /* For type 11h bits 6 and 7 are reserved */ + ivhd_11->flags = ivrs_agesa->ivhd.flags & 0x3f; + ivhd_11->length = sizeof(struct acpi_ivrs_ivhd_11); + /* BDF <bus>:00.2 */ + ivhd_11->device_id = 0x02 | (nb_dev->bus->secondary << 8); + /* PCI Capability block 0x40 (type 0xf, "Secure device") */ + ivhd_11->capability_offset = 0x40; + ivhd_11->iommu_base_low = ivrs_agesa->ivhd.iommu_base_low; + ivhd_11->iommu_base_high = ivrs_agesa->ivhd.iommu_base_high; + ivhd_11->pci_segment_group = 0x0000; + ivhd_11->iommu_info = ivrs_agesa->ivhd.iommu_info; + ivhd_11->iommu_attributes.perf_counters = + (IOMMU_MMIO32(ivhd_11->iommu_base_low + 0x4000) >> 7) & 0xf; + ivhd_11->iommu_attributes.perf_counter_banks = + (IOMMU_MMIO32(ivhd_11->iommu_base_low + 0x4000) >> 12) & 0x3f; + ivhd_11->iommu_attributes.msi_num_ppr = + (pci_read_config32(iommu_dev, ivhd_11->capability_offset + 0x10) >> 27) & 0x1f; + + if (pci_read_config32(iommu_dev, ivhd_11->capability_offset) & EFR_SUPPORT) { + ivhd_11->efr_reg_image_low = IOMMU_MMIO32(ivhd_11->iommu_base_low + 0x30); + ivhd_11->efr_reg_image_high = IOMMU_MMIO32(ivhd_11->iommu_base_low + 0x34); + } + + current += sizeof(acpi_ivrs_ivhd11_t); + + /* Now repeat all the device entries from type 10h */ + current_backup = current; + current = ivhd_dev_range(current, PCI_DEVFN(1, 0), PCI_DEVFN(0x1f, 6), 0); + ivhd_11->length += (current - current_backup); + add_ivhd_device_entries(NULL, all_devices, 0, -1, NULL, ¤t, &ivhd_11->length); + + /* Describe HPET */ + current_backup = current; + current = ivhd_describe_hpet(current); + ivhd_11->length += (current - current_backup); + + /* Describe IOAPICs */ + current_backup = current; + current = acpi_fill_ivrs_ioapic(ivrs_agesa, current); + ivhd_11->length += (current - current_backup); return current; } static unsigned long acpi_fill_ivrs(acpi_ivrs_t *ivrs, unsigned long current) { - uint8_t *p; acpi_ivrs_t *ivrs_agesa; + unsigned long current_backup; - struct device *nb_dev = pcidev_on_root(0x0, 0); + struct device *nb_dev = pcidev_on_root(0, 0); if (!nb_dev) { - printk(BIOS_WARNING, "%s: G-series northbridge device not present!\n", __func__); printk(BIOS_WARNING, "%s: IVRS table not generated...\n", __func__); return (unsigned long)ivrs; } + struct device *iommu_dev = pcidev_on_root(0, 2); + + if (!iommu_dev) { + printk(BIOS_WARNING, "%s: IOMMU device not found\n", __func__); + + return (unsigned long)ivrs; + } - /* obtain IOMMU base address */ ivrs_agesa = agesawrapper_getlateinitptr(PICK_IVRS); if (ivrs_agesa != NULL) { - ivrs->iv_info = 0x0; - /* Maximum supported virtual address size */ - ivrs->iv_info |= (0x40 << 15); - /* Maximum supported physical address size */ - ivrs->iv_info |= (0x30 << 8); - /* Guest virtual address width */ - ivrs->iv_info |= (0x2 << 5); - - ivrs->ivhd.type = 0x10; - ivrs->ivhd.flags = 0x0e; - /* Enable ATS support */ - ivrs->ivhd.flags |= 0x10; + ivrs->iv_info = ivrs_agesa->iv_info; + ivrs->ivhd.type = IVHD_BLOCK_TYPE_LEGACY__FIXED; + ivrs->ivhd.flags = ivrs_agesa->ivhd.flags; ivrs->ivhd.length = sizeof(struct acpi_ivrs_ivhd); /* BDF <bus>:00.2 */ - ivrs->ivhd.device_id = 0x2 | (nb_dev->bus->secondary << 8); - /* Capability block 0x40 (type 0xf, "Secure device") */ + ivrs->ivhd.device_id = 0x02 | (nb_dev->bus->secondary << 8); + /* PCI Capability block 0x40 (type 0xf, "Secure device") */ ivrs->ivhd.capability_offset = 0x40; ivrs->ivhd.iommu_base_low = ivrs_agesa->ivhd.iommu_base_low; ivrs->ivhd.iommu_base_high = ivrs_agesa->ivhd.iommu_base_high; - ivrs->ivhd.pci_segment_group = 0x0; - ivrs->ivhd.iommu_info = 0x0; - ivrs->ivhd.iommu_info |= (0x13 << 8); - /* use only performance counters related bits: - * PNCounters[16:13] and - * PNBanks[22:17], - * otherwise 0 */ - ivrs->ivhd.iommu_feature_info = - ivrs_agesa->ivhd.iommu_feature_info & 0x7fe000; + ivrs->ivhd.pci_segment_group = 0x0000; + ivrs->ivhd.iommu_info = ivrs_agesa->ivhd.iommu_info; + ivrs->ivhd.iommu_feature_info = ivrs_agesa->ivhd.iommu_feature_info; + /* Enable EFR if supported */ + if (pci_read_config32(iommu_dev, ivrs->ivhd.capability_offset) & EFR_SUPPORT) + ivrs->iv_info |= IVINFO_EFR_SUPPORTED; } else { printk(BIOS_WARNING, "%s: AGESA returned NULL IVRS\n", __func__); return (unsigned long)ivrs; } + /* + * Add all possible PCI devices on bus 0 that can generate transactions + * processed by IOMMU. Start with device 00:01.0 since IOMMU does not + * translate transactions generated by itself. + */ + current_backup = current; + current = ivhd_dev_range(current, PCI_DEVFN(1, 0), PCI_DEVFN(0x1f, 6), 0); + ivrs->ivhd.length += (current - current_backup); + add_ivhd_device_entries(NULL, all_devices, 0, -1, NULL, ¤t, &ivrs->ivhd.length); + /* Describe HPET */ - p = (uint8_t *)current; - p[0] = 0x48; /* Entry type */ - p[1] = 0; /* Device */ - p[2] = 0; /* Bus */ - p[3] = 0xd7; /* Data */ - p[4] = 0x0; /* HPET number */ - p[5] = 0x14 << 3; /* HPET device */ - p[6] = nb_dev->bus->secondary; /* HPET bus */ - p[7] = 0x2; /* Variety */ - ivrs->ivhd.length += 8; - current += 8; - - /* Describe PCI devices */ - int8_t root_level = -1; - add_ivrs_device_entries(NULL, all_devices, 0, -1, &root_level, ¤t, - &ivrs->ivhd.length); + current_backup = current; + current = ivhd_describe_hpet(current); + ivrs->ivhd.length += (current - current_backup); /* Describe IOAPICs */ - unsigned long prev_current = current; - current = acpi_fill_ivrs_ioapic(ivrs, current); - ivrs->ivhd.length += (current - prev_current); + current_backup = current; + current = acpi_fill_ivrs_ioapic(ivrs_agesa, current); + ivrs->ivhd.length += (current - current_backup); - return current; + /* If EFR is not supported, IVHD type 11h is reserved */ + if (!(ivrs->iv_info & IVINFO_EFR_SUPPORTED)) + return current; + + return acpi_fill_ivrs11(current, ivrs_agesa); } static void northbridge_fill_ssdt_generator(struct device *device) |