soc/amd/picasso: Fix ACPI PCI routing table
The original routing table did not handle all 8 INTx interrupts. Additionally it also didn't take the swizzling into account. Now that we know how AGESA programs the routing table we can correctly generate it. We still route the PCI interrupts through the FCH IOAPIC. A follow up will have the GNB IOAPIC handle the PCI interrupts. There is still work to be done to fix the legacy PCI_IRQ register for each PCI device. We can then remove the mainboard_pirq_data from each mainboard. BUG=b:170595019 TEST=Used ezkinil Boot kernel with `pci=nomsi amd_iommu=off noapic` and `pci=nomsi amd_iommu=off` then verified system was usable and verified /proc/interrupts looked correct. Signed-off-by: Raul E Rangel <rrangel@chromium.org> Change-Id: I2b2cce9913081d5cd456043ba619a79c1dfd4a8e Reviewed-on: https://review.coreboot.org/c/coreboot/+/48632 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Nikolai Vyssotski <nikolai.vyssotski@amd.corp-partner.google.com> Reviewed-by: Felix Held <felix-coreboot@felixheld.de>
This commit is contained in:
parent
9541d1792a
commit
2f5fd11474
|
@ -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 },
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <acpi/acpigen.h>
|
||||
#include <assert.h>
|
||||
#include <device/device.h>
|
||||
#include <device/pci.h>
|
||||
#include <device/pciexp.h>
|
||||
|
@ -8,6 +9,50 @@
|
|||
#include <soc/pci_devs.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/**
|
||||
* 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 = {
|
||||
|
|
Loading…
Reference in New Issue