Current logic does not parse the _PRT object inside the DSDT of PCI host bridge, and result in failing to route PCI legacy interrupt. This patch adds the logic to do so.
Signed-off-by: Suravee Suthikulpanit Suravee.Suthikulpanit@amd.com --- drivers/acpi/pci_irq.c | 46 +++++++++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 13 deletions(-)
diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c index b1def41..671c92c 100644 --- a/drivers/acpi/pci_irq.c +++ b/drivers/acpi/pci_irq.c @@ -157,13 +157,13 @@ static void do_prt_fixups(struct acpi_prt_entry *entry, } }
-static int acpi_pci_irq_check_entry(acpi_handle handle, struct pci_dev *dev, +static int acpi_pci_irq_check_entry(acpi_handle handle, + struct pci_bus *pbus, int device, int pin, struct acpi_pci_routing_table *prt, struct acpi_prt_entry **entry_ptr) { - int segment = pci_domain_nr(dev->bus); - int bus = dev->bus->number; - int device = PCI_SLOT(dev->devfn); + int segment = pci_domain_nr(pbus); + int bus = pbus->number; struct acpi_prt_entry *entry;
if (((prt->address >> 16) & 0xffff) != device || @@ -223,7 +223,7 @@ static int acpi_pci_irq_check_entry(acpi_handle handle, struct pci_dev *dev, return 0; }
-static int acpi_pci_irq_find_prt_entry(struct pci_dev *dev, +static int acpi_pci_irq_find_prt_entry(struct pci_bus *bus, int devid, int pin, struct acpi_prt_entry **entry_ptr) { acpi_status status; @@ -231,8 +231,8 @@ static int acpi_pci_irq_find_prt_entry(struct pci_dev *dev, struct acpi_pci_routing_table *entry; acpi_handle handle = NULL;
- if (dev->bus->bridge) - handle = ACPI_HANDLE(dev->bus->bridge); + if (bus->bridge) + handle = ACPI_HANDLE(bus->bridge);
if (!handle) return -ENODEV; @@ -246,8 +246,8 @@ static int acpi_pci_irq_find_prt_entry(struct pci_dev *dev,
entry = buffer.pointer; while (entry && (entry->length > 0)) { - if (!acpi_pci_irq_check_entry(handle, dev, pin, - entry, entry_ptr)) + if (!acpi_pci_irq_check_entry(handle, bus, devid, + pin, entry, entry_ptr)) break; entry = (struct acpi_pci_routing_table *) ((unsigned long)entry + entry->length); @@ -322,7 +322,7 @@ static struct acpi_prt_entry *acpi_pci_irq_lookup(struct pci_dev *dev, int pin) u8 bridge_pin, orig_pin = pin; int ret;
- ret = acpi_pci_irq_find_prt_entry(dev, pin, &entry); + ret = acpi_pci_irq_find_prt_entry(dev->bus, PCI_SLOT(dev->devfn), pin, &entry); if (!ret && entry) { #ifdef CONFIG_X86_IO_APIC acpi_reroute_boot_interrupt(dev, entry); @@ -336,7 +336,7 @@ static struct acpi_prt_entry *acpi_pci_irq_lookup(struct pci_dev *dev, int pin) * Attempt to derive an IRQ for this device from a parent bridge's * PCI interrupt routing entry (eg. yenta bridge and add-in card bridge). */ - bridge = dev->bus->self; + bridge = pci_upstream_bridge(dev); while (bridge) { pin = pci_swizzle_interrupt_pin(dev, pin);
@@ -352,7 +352,7 @@ static struct acpi_prt_entry *acpi_pci_irq_lookup(struct pci_dev *dev, int pin) pin = bridge_pin; }
- ret = acpi_pci_irq_find_prt_entry(bridge, pin, &entry); + ret = acpi_pci_irq_find_prt_entry(bridge->bus, PCI_SLOT(bridge->devfn), pin, &entry); if (!ret && entry) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Derived GSI for %s INT %c from %s\n", @@ -362,7 +362,27 @@ static struct acpi_prt_entry *acpi_pci_irq_lookup(struct pci_dev *dev, int pin) }
dev = bridge; - bridge = dev->bus->self; + bridge = pci_upstream_bridge(dev); + } + + if (!bridge) { + struct device *hb_dev = pci_get_host_bridge_device(dev); + struct pci_host_bridge *h_br = NULL; + + if (hb_dev) + h_br = to_pci_host_bridge(hb_dev); + + if (h_br) + ret = acpi_pci_irq_find_prt_entry(h_br->bus, 0, pin, + &entry); + pci_put_host_bridge_device(hdev); + if (!ret && entry) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Derived GSI for %s INT %c from %s\n", + pci_name(dev), pin_name(orig_pin), + "host bridge")); + return entry; + } }
dev_warn(&dev->dev, "can't derive routing for PCI INT %c\n",