diff --git a/src/soc/amd/picasso/acpi/northbridge.asl b/src/soc/amd/picasso/acpi/northbridge.asl index a81d3b27be..f6e198f5f2 100644 --- a/src/soc/amd/picasso/acpi/northbridge.asl +++ b/src/soc/amd/picasso/acpi/northbridge.asl @@ -26,18 +26,6 @@ Name(PR0, Package(){ Package() { 0x0000FFFF, 0, INTC, 0 }, Package() { 0x0000FFFF, 0, INTD, 0 }, - /* Bus 0, Dev 0x01 - F[1-7]: GPP PCI Bridges */ - Package() { 0x0001FFFF, 0, INTA, 0 }, - Package() { 0x0001FFFF, 1, INTB, 0 }, - Package() { 0x0001FFFF, 2, INTC, 0 }, - Package() { 0x0001FFFF, 3, INTD, 0 }, - - /* Bus 0, Dev 0x08 - F[1:PCI Bridge to Bus A, 2: PCI Bridge to Bus B] */ - Package() { 0x0008FFFF, 0, INTA, 0 }, - Package() { 0x0008FFFF, 1, INTB, 0 }, - Package() { 0x0008FFFF, 2, INTC, 0 }, - Package() { 0x0008FFFF, 3, INTD, 0 }, - /* Bus 0, Dev 0x14 - F[0:SMBus 3:LPC] */ Package() { 0x0014FFFF, 0, INTA, 0 }, Package() { 0x0014FFFF, 1, INTB, 0 }, diff --git a/src/soc/amd/picasso/acpi/pci_int.asl b/src/soc/amd/picasso/acpi/pci_int.asl index 8114c52043..feaec12d29 100644 --- a/src/soc/amd/picasso/acpi/pci_int.asl +++ b/src/soc/amd/picasso/acpi/pci_int.asl @@ -104,3 +104,7 @@ PCI_LINK(INTA, PIRA, IORA) PCI_LINK(INTB, PIRB, IORB) PCI_LINK(INTC, PIRC, IORC) PCI_LINK(INTD, PIRD, IORD) +PCI_LINK(INTE, PIRE, IORE) +PCI_LINK(INTF, PIRF, IORF) +PCI_LINK(INTG, PIRG, IORG) +PCI_LINK(INTH, PIRH, IORH) diff --git a/src/soc/amd/picasso/pcie_gpp.c b/src/soc/amd/picasso/pcie_gpp.c index 3c3021e086..1b651182a4 100644 --- a/src/soc/amd/picasso/pcie_gpp.c +++ b/src/soc/amd/picasso/pcie_gpp.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #include +#include #include #include #include @@ -8,6 +9,50 @@ #include #include +/** + * Each PCI bridge has its INTx lines routed to one of the 8 GNB IOAPIC PCI + * groups. Each group has 4 interrupts. The INTx lines can be swizzled before + * being routed to the IOAPIC. If the IOAPIC redirection entry is masked, the + * interrupt is reduced modulo 8 onto INT[A-H] and forwarded to the FCH IOAPIC. + **/ +struct pci_routing { + unsigned int devfn; + unsigned int group; + const char intx[5]; +}; + +/* See AMD PPR 55570 - IOAPIC Initialization for the table that AGESA sets up */ +static const struct pci_routing pci_routing_table[] = { + {PCIE_GPP_0_DEVFN, 0, "ABCD"}, + {PCIE_GPP_1_DEVFN, 1, "ABCD"}, + {PCIE_GPP_2_DEVFN, 2, "ABCD"}, + {PCIE_GPP_3_DEVFN, 3, "ABCD"}, + {PCIE_GPP_4_DEVFN, 4, "ABCD"}, + {PCIE_GPP_5_DEVFN, 5, "ABCD"}, + {PCIE_GPP_6_DEVFN, 6, "ABCD"}, + {PCIE_GPP_A_DEVFN, 7, "ABCD"}, + {PCIE_GPP_B_DEVFN, 7, "CDAB"}, +}; + +static const struct pci_routing *get_pci_routing(unsigned int devfn) +{ + for (size_t i = 0; i < ARRAY_SIZE(pci_routing_table); ++i) { + if (devfn == pci_routing_table[i].devfn) + return &pci_routing_table[i]; + } + + return NULL; +} + +static unsigned int calculate_irq(const struct pci_routing *pci_routing, unsigned int i) +{ + unsigned int irq_index; + irq_index = pci_routing->group * 4; + irq_index += pci_routing->intx[i] - 'A'; + + return irq_index; +} + static const char *pcie_gpp_acpi_name(const struct device *dev) { if (dev->path.type != DEVICE_PATH_PCI) @@ -37,6 +82,113 @@ static const char *pcie_gpp_acpi_name(const struct device *dev) return NULL; } +static void acpigen_write_PRT(const struct device *dev) +{ + char link_template[] = "\\_SB.INTX"; + unsigned int irq_index; + + const struct pci_routing *pci_routing = get_pci_routing(dev->path.pci.devfn); + if (!pci_routing) { + printk(BIOS_ERR, "PCI routing table not found for %s\n", dev_path(dev)); + return; + } + + acpigen_write_method("_PRT", 0); + acpigen_emit_byte(RETURN_OP); + + acpigen_write_package(4); + for (unsigned int i = 0; i < 4; ++i) { + irq_index = calculate_irq(pci_routing, i); + + link_template[8] = 'A' + (irq_index % 8); + + acpigen_write_package(4); + acpigen_write_dword(0x0000FFFF); + acpigen_write_byte(i); + acpigen_emit_namestring(link_template); + acpigen_write_dword(0); + acpigen_pop_len(); + } + acpigen_pop_len(); /* Package */ + + acpigen_pop_len(); /* Method */ +} + +/* + * This function writes a PCI device with _ADR, _STA, and _PRT objects: + * Example: + * Scope (\_SB.PCI0) + * { + * Device (PBRA) + * { + * Name (_ADR, 0x0000000000080001) // _ADR: Address + * Method (_STA, 0, NotSerialized) // _STA: Status + * { + * Return (0x0F) + * } + * + * Method (_PRT, 0, NotSerialized) // _PRT: PCI Routing Table + * { + * Return (Package (0x04) + * { + * Package (0x04) + * { + * 0x0000FFFF, + * 0x00, + * \_SB.INTE, + * 0x00000000 + * }, + * + * Package (0x04) + * { + * 0x0000FFFF, + * 0x01, + * \_SB.INTF, + * 0x00000000 + * }, + * + * Package (0x04) + * { + * 0x0000FFFF, + * 0x02, + * \_SB.INTG, + * 0x00000000 + * }, + * + * Package (0x04) + * { + * 0x0000FFFF, + * 0x03, + * \_SB.INTH, + * 0x00000000 + * } + * }) + * } + * } + * } + */ +static void acpi_device_write_gpp_pci_dev(const struct device *dev) +{ + const char *scope = acpi_device_scope(dev); + const char *name = acpi_device_name(dev); + + assert(dev->path.type == DEVICE_PATH_PCI); + assert(name); + assert(scope); + + acpigen_write_scope(scope); + acpigen_write_device(name); + + acpigen_write_ADR_pci_device(dev); + acpigen_write_STA(acpi_device_status(dev)); + + acpigen_write_PRT(dev); + + acpigen_pop_len(); /* Device */ + acpigen_pop_len(); /* Scope */ +} + + static struct device_operations internal_pcie_gpp_ops = { .read_resources = pci_bus_read_resources, .set_resources = pci_dev_set_resources, @@ -44,7 +196,7 @@ static struct device_operations internal_pcie_gpp_ops = { .scan_bus = pci_scan_bridge, .reset_bus = pci_bus_reset, .acpi_name = pcie_gpp_acpi_name, - .acpi_fill_ssdt = acpi_device_write_pci_dev, + .acpi_fill_ssdt = acpi_device_write_gpp_pci_dev, }; static const unsigned short pci_device_ids[] = { @@ -66,7 +218,7 @@ static struct device_operations external_pcie_gpp_ops = { .scan_bus = pciexp_scan_bridge, .reset_bus = pci_bus_reset, .acpi_name = pcie_gpp_acpi_name, - .acpi_fill_ssdt = acpi_device_write_pci_dev, + .acpi_fill_ssdt = acpi_device_write_gpp_pci_dev, }; static const struct pci_driver external_pcie_gpp_driver __pci_driver = {