/* SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include #include #include #include #include #include #include #include #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; uint8_t pin[4]; }; /* 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, {PIRQ_A, PIRQ_B, PIRQ_C, PIRQ_D} }, {PCIE_GPP_1_DEVFN, 1, {PIRQ_A, PIRQ_B, PIRQ_C, PIRQ_D} }, {PCIE_GPP_2_DEVFN, 2, {PIRQ_A, PIRQ_B, PIRQ_C, PIRQ_D} }, {PCIE_GPP_3_DEVFN, 3, {PIRQ_A, PIRQ_B, PIRQ_C, PIRQ_D} }, {PCIE_GPP_4_DEVFN, 4, {PIRQ_A, PIRQ_B, PIRQ_C, PIRQ_D} }, {PCIE_GPP_5_DEVFN, 5, {PIRQ_A, PIRQ_B, PIRQ_C, PIRQ_D} }, {PCIE_GPP_6_DEVFN, 6, {PIRQ_A, PIRQ_B, PIRQ_C, PIRQ_D} }, {PCIE_GPP_A_DEVFN, 7, {PIRQ_A, PIRQ_B, PIRQ_C, PIRQ_D} }, {PCIE_GPP_B_DEVFN, 7, {PIRQ_C, PIRQ_D, PIRQ_A, PIRQ_B} }, }; /* * This data structure is populated from the raw data above. It is used * by amd/common/block/pci/amd_pci_util to write the PCI_INT_LINE register * to each PCI device. */ static struct pirq_struct pirq_data[ARRAY_SIZE(pci_routing_table)]; 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->pin[i]; return irq_index; } void populate_pirq_data(void) { const struct pci_routing *pci_routing; struct pirq_struct *pirq; unsigned int irq_index; for (size_t i = 0; i < ARRAY_SIZE(pirq_data); ++i) { pirq = &pirq_data[i]; pci_routing = &pci_routing_table[i]; pirq->devfn = pci_routing->devfn; for (size_t j = 0; j < 4; ++j) { irq_index = calculate_irq(pci_routing, j); pirq->PIN[j] = irq_index % 8; } } pirq_data_ptr = pirq_data; pirq_data_size = ARRAY_SIZE(pirq_data); } static const char *pcie_gpp_acpi_name(const struct device *dev) { if (dev->path.type != DEVICE_PATH_PCI) return NULL; switch (dev->path.pci.devfn) { case PCIE_GPP_0_DEVFN: return "PBR0"; case PCIE_GPP_1_DEVFN: return "PBR1"; case PCIE_GPP_2_DEVFN: return "PBR2"; case PCIE_GPP_3_DEVFN: return "PBR3"; case PCIE_GPP_4_DEVFN: return "PBR4"; case PCIE_GPP_5_DEVFN: return "PBR5"; case PCIE_GPP_6_DEVFN: return "PBR6"; case PCIE_GPP_A_DEVFN: return "PBRA"; case PCIE_GPP_B_DEVFN: return "PBRB"; } 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); /* If (PICM) */ acpigen_write_if(); acpigen_emit_namestring("PICM"); /* Return (Package{...}) */ acpigen_emit_byte(RETURN_OP); acpigen_write_package(4); /* Package - APIC Routing */ for (unsigned int i = 0; i < 4; ++i) { irq_index = calculate_irq(pci_routing, i); acpigen_write_package(4); /* There is only one device attached to the bridge */ acpigen_write_dword(0x0000FFFF); acpigen_write_byte(i); acpigen_write_byte(0); /* Source: GSI */ /* GNB IO-APIC is located after the FCH IO-APIC */ acpigen_write_dword(IO_APIC_INTERRUPTS + irq_index); acpigen_pop_len(); } acpigen_pop_len(); /* Package - APIC Routing */ /* Else */ acpigen_write_else(); /* Return (Package{...}) */ acpigen_emit_byte(RETURN_OP); acpigen_write_package(4); /* Package - PIC Routing */ 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); /* There is only one device attached to the bridge */ acpigen_write_dword(0x0000FFFF); acpigen_write_byte(i); acpigen_emit_namestring(link_template); acpigen_write_dword(0); acpigen_pop_len(); } acpigen_pop_len(); /* Package - PIC Routing */ acpigen_pop_len(); /* End Else */ 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 * { * If (PICM) * { * Return (Package (0x04) * { * Package (0x04) * { * 0x0000FFFF, * 0x00, * 0x00, * 0x00000034 * }, * * Package (0x04) * { * 0x0000FFFF, * 0x01, * 0x00, * 0x00000035 * }, * * Package (0x04) * { * 0x0000FFFF, * 0x02, * 0x00, * 0x00000036 * }, * * Package (0x04) * { * 0x0000FFFF, * 0x03, * 0x00, * 0x00000037 * } * }) * } * Else * { * 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, .enable_resources = pci_bus_enable_resources, .scan_bus = pci_scan_bridge, .reset_bus = pci_bus_reset, .acpi_name = pcie_gpp_acpi_name, .acpi_fill_ssdt = acpi_device_write_gpp_pci_dev, }; static const unsigned short pci_device_ids[] = { PCI_DEVICE_ID_AMD_FAM17H_MODEL18H_PCIE_GPP_BUSA, PCI_DEVICE_ID_AMD_FAM17H_MODEL18H_PCIE_GPP_BUSB, 0 }; static const struct pci_driver internal_pcie_gpp_driver __pci_driver = { .ops = &internal_pcie_gpp_ops, .vendor = PCI_VENDOR_ID_AMD, .devices = pci_device_ids, }; static struct device_operations external_pcie_gpp_ops = { .read_resources = pci_bus_read_resources, .set_resources = pci_dev_set_resources, .enable_resources = pci_bus_enable_resources, .scan_bus = pciexp_scan_bridge, .reset_bus = pci_bus_reset, .acpi_name = pcie_gpp_acpi_name, .acpi_fill_ssdt = acpi_device_write_gpp_pci_dev, }; static const struct pci_driver external_pcie_gpp_driver __pci_driver = { .ops = &external_pcie_gpp_ops, .vendor = PCI_VENDOR_ID_AMD, .device = PCI_DEVICE_ID_AMD_FAM17H_MODEL18H_PCIE_GPP, };