18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * pci_irq.c - ACPI PCI Interrupt Routing ($Revision: 11 $) 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> 68c2ecf20Sopenharmony_ci * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> 78c2ecf20Sopenharmony_ci * Copyright (C) 2002 Dominik Brodowski <devel@brodo.de> 88c2ecf20Sopenharmony_ci * (c) Copyright 2008 Hewlett-Packard Development Company, L.P. 98c2ecf20Sopenharmony_ci * Bjorn Helgaas <bjorn.helgaas@hp.com> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/dmi.h> 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/init.h> 178c2ecf20Sopenharmony_ci#include <linux/types.h> 188c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 198c2ecf20Sopenharmony_ci#include <linux/pm.h> 208c2ecf20Sopenharmony_ci#include <linux/pci.h> 218c2ecf20Sopenharmony_ci#include <linux/acpi.h> 228c2ecf20Sopenharmony_ci#include <linux/slab.h> 238c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define PREFIX "ACPI: " 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define _COMPONENT ACPI_PCI_COMPONENT 288c2ecf20Sopenharmony_ciACPI_MODULE_NAME("pci_irq"); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistruct acpi_prt_entry { 318c2ecf20Sopenharmony_ci struct acpi_pci_id id; 328c2ecf20Sopenharmony_ci u8 pin; 338c2ecf20Sopenharmony_ci acpi_handle link; 348c2ecf20Sopenharmony_ci u32 index; /* GSI, or link _CRS index */ 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic inline char pin_name(int pin) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci return 'A' + pin - 1; 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- 438c2ecf20Sopenharmony_ci PCI IRQ Routing Table (PRT) Support 448c2ecf20Sopenharmony_ci -------------------------------------------------------------------------- */ 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* http://bugzilla.kernel.org/show_bug.cgi?id=4773 */ 478c2ecf20Sopenharmony_cistatic const struct dmi_system_id medion_md9580[] = { 488c2ecf20Sopenharmony_ci { 498c2ecf20Sopenharmony_ci .ident = "Medion MD9580-F laptop", 508c2ecf20Sopenharmony_ci .matches = { 518c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"), 528c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "A555"), 538c2ecf20Sopenharmony_ci }, 548c2ecf20Sopenharmony_ci }, 558c2ecf20Sopenharmony_ci { } 568c2ecf20Sopenharmony_ci}; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* http://bugzilla.kernel.org/show_bug.cgi?id=5044 */ 598c2ecf20Sopenharmony_cistatic const struct dmi_system_id dell_optiplex[] = { 608c2ecf20Sopenharmony_ci { 618c2ecf20Sopenharmony_ci .ident = "Dell Optiplex GX1", 628c2ecf20Sopenharmony_ci .matches = { 638c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), 648c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex GX1 600S+"), 658c2ecf20Sopenharmony_ci }, 668c2ecf20Sopenharmony_ci }, 678c2ecf20Sopenharmony_ci { } 688c2ecf20Sopenharmony_ci}; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* http://bugzilla.kernel.org/show_bug.cgi?id=10138 */ 718c2ecf20Sopenharmony_cistatic const struct dmi_system_id hp_t5710[] = { 728c2ecf20Sopenharmony_ci { 738c2ecf20Sopenharmony_ci .ident = "HP t5710", 748c2ecf20Sopenharmony_ci .matches = { 758c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), 768c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "hp t5000 series"), 778c2ecf20Sopenharmony_ci DMI_MATCH(DMI_BOARD_NAME, "098Ch"), 788c2ecf20Sopenharmony_ci }, 798c2ecf20Sopenharmony_ci }, 808c2ecf20Sopenharmony_ci { } 818c2ecf20Sopenharmony_ci}; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistruct prt_quirk { 848c2ecf20Sopenharmony_ci const struct dmi_system_id *system; 858c2ecf20Sopenharmony_ci unsigned int segment; 868c2ecf20Sopenharmony_ci unsigned int bus; 878c2ecf20Sopenharmony_ci unsigned int device; 888c2ecf20Sopenharmony_ci unsigned char pin; 898c2ecf20Sopenharmony_ci const char *source; /* according to BIOS */ 908c2ecf20Sopenharmony_ci const char *actual_source; 918c2ecf20Sopenharmony_ci}; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci#define PCI_INTX_PIN(c) (c - 'A' + 1) 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci/* 968c2ecf20Sopenharmony_ci * These systems have incorrect _PRT entries. The BIOS claims the PCI 978c2ecf20Sopenharmony_ci * interrupt at the listed segment/bus/device/pin is connected to the first 988c2ecf20Sopenharmony_ci * link device, but it is actually connected to the second. 998c2ecf20Sopenharmony_ci */ 1008c2ecf20Sopenharmony_cistatic const struct prt_quirk prt_quirks[] = { 1018c2ecf20Sopenharmony_ci { medion_md9580, 0, 0, 9, PCI_INTX_PIN('A'), 1028c2ecf20Sopenharmony_ci "\\_SB_.PCI0.ISA_.LNKA", 1038c2ecf20Sopenharmony_ci "\\_SB_.PCI0.ISA_.LNKB"}, 1048c2ecf20Sopenharmony_ci { dell_optiplex, 0, 0, 0xd, PCI_INTX_PIN('A'), 1058c2ecf20Sopenharmony_ci "\\_SB_.LNKB", 1068c2ecf20Sopenharmony_ci "\\_SB_.LNKA"}, 1078c2ecf20Sopenharmony_ci { hp_t5710, 0, 0, 1, PCI_INTX_PIN('A'), 1088c2ecf20Sopenharmony_ci "\\_SB_.PCI0.LNK1", 1098c2ecf20Sopenharmony_ci "\\_SB_.PCI0.LNK3"}, 1108c2ecf20Sopenharmony_ci}; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic void do_prt_fixups(struct acpi_prt_entry *entry, 1138c2ecf20Sopenharmony_ci struct acpi_pci_routing_table *prt) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci int i; 1168c2ecf20Sopenharmony_ci const struct prt_quirk *quirk; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(prt_quirks); i++) { 1198c2ecf20Sopenharmony_ci quirk = &prt_quirks[i]; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci /* All current quirks involve link devices, not GSIs */ 1228c2ecf20Sopenharmony_ci if (dmi_check_system(quirk->system) && 1238c2ecf20Sopenharmony_ci entry->id.segment == quirk->segment && 1248c2ecf20Sopenharmony_ci entry->id.bus == quirk->bus && 1258c2ecf20Sopenharmony_ci entry->id.device == quirk->device && 1268c2ecf20Sopenharmony_ci entry->pin == quirk->pin && 1278c2ecf20Sopenharmony_ci !strcmp(prt->source, quirk->source) && 1288c2ecf20Sopenharmony_ci strlen(prt->source) >= strlen(quirk->actual_source)) { 1298c2ecf20Sopenharmony_ci printk(KERN_WARNING PREFIX "firmware reports " 1308c2ecf20Sopenharmony_ci "%04x:%02x:%02x PCI INT %c connected to %s; " 1318c2ecf20Sopenharmony_ci "changing to %s\n", 1328c2ecf20Sopenharmony_ci entry->id.segment, entry->id.bus, 1338c2ecf20Sopenharmony_ci entry->id.device, pin_name(entry->pin), 1348c2ecf20Sopenharmony_ci prt->source, quirk->actual_source); 1358c2ecf20Sopenharmony_ci strcpy(prt->source, quirk->actual_source); 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic int acpi_pci_irq_check_entry(acpi_handle handle, struct pci_dev *dev, 1418c2ecf20Sopenharmony_ci int pin, struct acpi_pci_routing_table *prt, 1428c2ecf20Sopenharmony_ci struct acpi_prt_entry **entry_ptr) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci int segment = pci_domain_nr(dev->bus); 1458c2ecf20Sopenharmony_ci int bus = dev->bus->number; 1468c2ecf20Sopenharmony_ci int device = pci_ari_enabled(dev->bus) ? 0 : PCI_SLOT(dev->devfn); 1478c2ecf20Sopenharmony_ci struct acpi_prt_entry *entry; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (((prt->address >> 16) & 0xffff) != device || 1508c2ecf20Sopenharmony_ci prt->pin + 1 != pin) 1518c2ecf20Sopenharmony_ci return -ENODEV; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci entry = kzalloc(sizeof(struct acpi_prt_entry), GFP_KERNEL); 1548c2ecf20Sopenharmony_ci if (!entry) 1558c2ecf20Sopenharmony_ci return -ENOMEM; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci /* 1588c2ecf20Sopenharmony_ci * Note that the _PRT uses 0=INTA, 1=INTB, etc, while PCI uses 1598c2ecf20Sopenharmony_ci * 1=INTA, 2=INTB. We use the PCI encoding throughout, so convert 1608c2ecf20Sopenharmony_ci * it here. 1618c2ecf20Sopenharmony_ci */ 1628c2ecf20Sopenharmony_ci entry->id.segment = segment; 1638c2ecf20Sopenharmony_ci entry->id.bus = bus; 1648c2ecf20Sopenharmony_ci entry->id.device = (prt->address >> 16) & 0xFFFF; 1658c2ecf20Sopenharmony_ci entry->pin = prt->pin + 1; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci do_prt_fixups(entry, prt); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci entry->index = prt->source_index; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* 1728c2ecf20Sopenharmony_ci * Type 1: Dynamic 1738c2ecf20Sopenharmony_ci * --------------- 1748c2ecf20Sopenharmony_ci * The 'source' field specifies the PCI interrupt link device used to 1758c2ecf20Sopenharmony_ci * configure the IRQ assigned to this slot|dev|pin. The 'source_index' 1768c2ecf20Sopenharmony_ci * indicates which resource descriptor in the resource template (of 1778c2ecf20Sopenharmony_ci * the link device) this interrupt is allocated from. 1788c2ecf20Sopenharmony_ci * 1798c2ecf20Sopenharmony_ci * NOTE: Don't query the Link Device for IRQ information at this time 1808c2ecf20Sopenharmony_ci * because Link Device enumeration may not have occurred yet 1818c2ecf20Sopenharmony_ci * (e.g. exists somewhere 'below' this _PRT entry in the ACPI 1828c2ecf20Sopenharmony_ci * namespace). 1838c2ecf20Sopenharmony_ci */ 1848c2ecf20Sopenharmony_ci if (prt->source[0]) 1858c2ecf20Sopenharmony_ci acpi_get_handle(handle, prt->source, &entry->link); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci /* 1888c2ecf20Sopenharmony_ci * Type 2: Static 1898c2ecf20Sopenharmony_ci * -------------- 1908c2ecf20Sopenharmony_ci * The 'source' field is NULL, and the 'source_index' field specifies 1918c2ecf20Sopenharmony_ci * the IRQ value, which is hardwired to specific interrupt inputs on 1928c2ecf20Sopenharmony_ci * the interrupt controller. 1938c2ecf20Sopenharmony_ci */ 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci ACPI_DEBUG_PRINT_RAW((ACPI_DB_INFO, 1968c2ecf20Sopenharmony_ci " %04x:%02x:%02x[%c] -> %s[%d]\n", 1978c2ecf20Sopenharmony_ci entry->id.segment, entry->id.bus, 1988c2ecf20Sopenharmony_ci entry->id.device, pin_name(entry->pin), 1998c2ecf20Sopenharmony_ci prt->source, entry->index)); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci *entry_ptr = entry; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci return 0; 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic int acpi_pci_irq_find_prt_entry(struct pci_dev *dev, 2078c2ecf20Sopenharmony_ci int pin, struct acpi_prt_entry **entry_ptr) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci acpi_status status; 2108c2ecf20Sopenharmony_ci struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 2118c2ecf20Sopenharmony_ci struct acpi_pci_routing_table *entry; 2128c2ecf20Sopenharmony_ci acpi_handle handle = NULL; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci if (dev->bus->bridge) 2158c2ecf20Sopenharmony_ci handle = ACPI_HANDLE(dev->bus->bridge); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci if (!handle) 2188c2ecf20Sopenharmony_ci return -ENODEV; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci /* 'handle' is the _PRT's parent (root bridge or PCI-PCI bridge) */ 2218c2ecf20Sopenharmony_ci status = acpi_get_irq_routing_table(handle, &buffer); 2228c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 2238c2ecf20Sopenharmony_ci kfree(buffer.pointer); 2248c2ecf20Sopenharmony_ci return -ENODEV; 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci entry = buffer.pointer; 2288c2ecf20Sopenharmony_ci while (entry && (entry->length > 0)) { 2298c2ecf20Sopenharmony_ci if (!acpi_pci_irq_check_entry(handle, dev, pin, 2308c2ecf20Sopenharmony_ci entry, entry_ptr)) 2318c2ecf20Sopenharmony_ci break; 2328c2ecf20Sopenharmony_ci entry = (struct acpi_pci_routing_table *) 2338c2ecf20Sopenharmony_ci ((unsigned long)entry + entry->length); 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci kfree(buffer.pointer); 2378c2ecf20Sopenharmony_ci return 0; 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- 2418c2ecf20Sopenharmony_ci PCI Interrupt Routing Support 2428c2ecf20Sopenharmony_ci -------------------------------------------------------------------------- */ 2438c2ecf20Sopenharmony_ci#ifdef CONFIG_X86_IO_APIC 2448c2ecf20Sopenharmony_ciextern int noioapicquirk; 2458c2ecf20Sopenharmony_ciextern int noioapicreroute; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic int bridge_has_boot_interrupt_variant(struct pci_bus *bus) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci struct pci_bus *bus_it; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci for (bus_it = bus ; bus_it ; bus_it = bus_it->parent) { 2528c2ecf20Sopenharmony_ci if (!bus_it->self) 2538c2ecf20Sopenharmony_ci return 0; 2548c2ecf20Sopenharmony_ci if (bus_it->self->irq_reroute_variant) 2558c2ecf20Sopenharmony_ci return bus_it->self->irq_reroute_variant; 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci return 0; 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci/* 2618c2ecf20Sopenharmony_ci * Some chipsets (e.g. Intel 6700PXH) generate a legacy INTx when the IRQ 2628c2ecf20Sopenharmony_ci * entry in the chipset's IO-APIC is masked (as, e.g. the RT kernel does 2638c2ecf20Sopenharmony_ci * during interrupt handling). When this INTx generation cannot be disabled, 2648c2ecf20Sopenharmony_ci * we reroute these interrupts to their legacy equivalent to get rid of 2658c2ecf20Sopenharmony_ci * spurious interrupts. 2668c2ecf20Sopenharmony_ci */ 2678c2ecf20Sopenharmony_cistatic int acpi_reroute_boot_interrupt(struct pci_dev *dev, 2688c2ecf20Sopenharmony_ci struct acpi_prt_entry *entry) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci if (noioapicquirk || noioapicreroute) { 2718c2ecf20Sopenharmony_ci return 0; 2728c2ecf20Sopenharmony_ci } else { 2738c2ecf20Sopenharmony_ci switch (bridge_has_boot_interrupt_variant(dev->bus)) { 2748c2ecf20Sopenharmony_ci case 0: 2758c2ecf20Sopenharmony_ci /* no rerouting necessary */ 2768c2ecf20Sopenharmony_ci return 0; 2778c2ecf20Sopenharmony_ci case INTEL_IRQ_REROUTE_VARIANT: 2788c2ecf20Sopenharmony_ci /* 2798c2ecf20Sopenharmony_ci * Remap according to INTx routing table in 6700PXH 2808c2ecf20Sopenharmony_ci * specs, intel order number 302628-002, section 2818c2ecf20Sopenharmony_ci * 2.15.2. Other chipsets (80332, ...) have the same 2828c2ecf20Sopenharmony_ci * mapping and are handled here as well. 2838c2ecf20Sopenharmony_ci */ 2848c2ecf20Sopenharmony_ci dev_info(&dev->dev, "PCI IRQ %d -> rerouted to legacy " 2858c2ecf20Sopenharmony_ci "IRQ %d\n", entry->index, 2868c2ecf20Sopenharmony_ci (entry->index % 4) + 16); 2878c2ecf20Sopenharmony_ci entry->index = (entry->index % 4) + 16; 2888c2ecf20Sopenharmony_ci return 1; 2898c2ecf20Sopenharmony_ci default: 2908c2ecf20Sopenharmony_ci dev_warn(&dev->dev, "Cannot reroute IRQ %d to legacy " 2918c2ecf20Sopenharmony_ci "IRQ: unknown mapping\n", entry->index); 2928c2ecf20Sopenharmony_ci return -1; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci#endif /* CONFIG_X86_IO_APIC */ 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic struct acpi_prt_entry *acpi_pci_irq_lookup(struct pci_dev *dev, int pin) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci struct acpi_prt_entry *entry = NULL; 3018c2ecf20Sopenharmony_ci struct pci_dev *bridge; 3028c2ecf20Sopenharmony_ci u8 bridge_pin, orig_pin = pin; 3038c2ecf20Sopenharmony_ci int ret; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci ret = acpi_pci_irq_find_prt_entry(dev, pin, &entry); 3068c2ecf20Sopenharmony_ci if (!ret && entry) { 3078c2ecf20Sopenharmony_ci#ifdef CONFIG_X86_IO_APIC 3088c2ecf20Sopenharmony_ci acpi_reroute_boot_interrupt(dev, entry); 3098c2ecf20Sopenharmony_ci#endif /* CONFIG_X86_IO_APIC */ 3108c2ecf20Sopenharmony_ci ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %s[%c] _PRT entry\n", 3118c2ecf20Sopenharmony_ci pci_name(dev), pin_name(pin))); 3128c2ecf20Sopenharmony_ci return entry; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci /* 3168c2ecf20Sopenharmony_ci * Attempt to derive an IRQ for this device from a parent bridge's 3178c2ecf20Sopenharmony_ci * PCI interrupt routing entry (eg. yenta bridge and add-in card bridge). 3188c2ecf20Sopenharmony_ci */ 3198c2ecf20Sopenharmony_ci bridge = dev->bus->self; 3208c2ecf20Sopenharmony_ci while (bridge) { 3218c2ecf20Sopenharmony_ci pin = pci_swizzle_interrupt_pin(dev, pin); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci if ((bridge->class >> 8) == PCI_CLASS_BRIDGE_CARDBUS) { 3248c2ecf20Sopenharmony_ci /* PC card has the same IRQ as its cardbridge */ 3258c2ecf20Sopenharmony_ci bridge_pin = bridge->pin; 3268c2ecf20Sopenharmony_ci if (!bridge_pin) { 3278c2ecf20Sopenharmony_ci ACPI_DEBUG_PRINT((ACPI_DB_INFO, 3288c2ecf20Sopenharmony_ci "No interrupt pin configured for device %s\n", 3298c2ecf20Sopenharmony_ci pci_name(bridge))); 3308c2ecf20Sopenharmony_ci return NULL; 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci pin = bridge_pin; 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci ret = acpi_pci_irq_find_prt_entry(bridge, pin, &entry); 3368c2ecf20Sopenharmony_ci if (!ret && entry) { 3378c2ecf20Sopenharmony_ci ACPI_DEBUG_PRINT((ACPI_DB_INFO, 3388c2ecf20Sopenharmony_ci "Derived GSI for %s INT %c from %s\n", 3398c2ecf20Sopenharmony_ci pci_name(dev), pin_name(orig_pin), 3408c2ecf20Sopenharmony_ci pci_name(bridge))); 3418c2ecf20Sopenharmony_ci return entry; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci dev = bridge; 3458c2ecf20Sopenharmony_ci bridge = dev->bus->self; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci dev_warn(&dev->dev, "can't derive routing for PCI INT %c\n", 3498c2ecf20Sopenharmony_ci pin_name(orig_pin)); 3508c2ecf20Sopenharmony_ci return NULL; 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_ISA) || IS_ENABLED(CONFIG_EISA) 3548c2ecf20Sopenharmony_cistatic int acpi_isa_register_gsi(struct pci_dev *dev) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci u32 dev_gsi; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci /* Interrupt Line values above 0xF are forbidden */ 3598c2ecf20Sopenharmony_ci if (dev->irq > 0 && (dev->irq <= 0xF) && 3608c2ecf20Sopenharmony_ci acpi_isa_irq_available(dev->irq) && 3618c2ecf20Sopenharmony_ci (acpi_isa_irq_to_gsi(dev->irq, &dev_gsi) == 0)) { 3628c2ecf20Sopenharmony_ci dev_warn(&dev->dev, "PCI INT %c: no GSI - using ISA IRQ %d\n", 3638c2ecf20Sopenharmony_ci pin_name(dev->pin), dev->irq); 3648c2ecf20Sopenharmony_ci acpi_register_gsi(&dev->dev, dev_gsi, 3658c2ecf20Sopenharmony_ci ACPI_LEVEL_SENSITIVE, 3668c2ecf20Sopenharmony_ci ACPI_ACTIVE_LOW); 3678c2ecf20Sopenharmony_ci return 0; 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci return -EINVAL; 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci#else 3728c2ecf20Sopenharmony_cistatic inline int acpi_isa_register_gsi(struct pci_dev *dev) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci return -ENODEV; 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci#endif 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_cistatic inline bool acpi_pci_irq_valid(struct pci_dev *dev, u8 pin) 3798c2ecf20Sopenharmony_ci{ 3808c2ecf20Sopenharmony_ci#ifdef CONFIG_X86 3818c2ecf20Sopenharmony_ci /* 3828c2ecf20Sopenharmony_ci * On x86 irq line 0xff means "unknown" or "no connection" 3838c2ecf20Sopenharmony_ci * (PCI 3.0, Section 6.2.4, footnote on page 223). 3848c2ecf20Sopenharmony_ci */ 3858c2ecf20Sopenharmony_ci if (dev->irq == 0xff) { 3868c2ecf20Sopenharmony_ci dev->irq = IRQ_NOTCONNECTED; 3878c2ecf20Sopenharmony_ci dev_warn(&dev->dev, "PCI INT %c: not connected\n", 3888c2ecf20Sopenharmony_ci pin_name(pin)); 3898c2ecf20Sopenharmony_ci return false; 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci#endif 3928c2ecf20Sopenharmony_ci return true; 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ciint acpi_pci_irq_enable(struct pci_dev *dev) 3968c2ecf20Sopenharmony_ci{ 3978c2ecf20Sopenharmony_ci struct acpi_prt_entry *entry; 3988c2ecf20Sopenharmony_ci int gsi; 3998c2ecf20Sopenharmony_ci u8 pin; 4008c2ecf20Sopenharmony_ci int triggering = ACPI_LEVEL_SENSITIVE; 4018c2ecf20Sopenharmony_ci /* 4028c2ecf20Sopenharmony_ci * On ARM systems with the GIC interrupt model, level interrupts 4038c2ecf20Sopenharmony_ci * are always polarity high by specification; PCI legacy 4048c2ecf20Sopenharmony_ci * IRQs lines are inverted before reaching the interrupt 4058c2ecf20Sopenharmony_ci * controller and must therefore be considered active high 4068c2ecf20Sopenharmony_ci * as default. 4078c2ecf20Sopenharmony_ci */ 4088c2ecf20Sopenharmony_ci int polarity = (acpi_irq_model == ACPI_IRQ_MODEL_GIC || 4098c2ecf20Sopenharmony_ci acpi_irq_model == ACPI_IRQ_MODEL_LPIC) ? 4108c2ecf20Sopenharmony_ci ACPI_ACTIVE_HIGH : ACPI_ACTIVE_LOW; 4118c2ecf20Sopenharmony_ci char *link = NULL; 4128c2ecf20Sopenharmony_ci char link_desc[16]; 4138c2ecf20Sopenharmony_ci int rc; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci pin = dev->pin; 4168c2ecf20Sopenharmony_ci if (!pin) { 4178c2ecf20Sopenharmony_ci ACPI_DEBUG_PRINT((ACPI_DB_INFO, 4188c2ecf20Sopenharmony_ci "No interrupt pin configured for device %s\n", 4198c2ecf20Sopenharmony_ci pci_name(dev))); 4208c2ecf20Sopenharmony_ci return 0; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci if (dev->irq_managed && dev->irq > 0) 4248c2ecf20Sopenharmony_ci return 0; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci entry = acpi_pci_irq_lookup(dev, pin); 4278c2ecf20Sopenharmony_ci if (!entry) { 4288c2ecf20Sopenharmony_ci /* 4298c2ecf20Sopenharmony_ci * IDE legacy mode controller IRQs are magic. Why do compat 4308c2ecf20Sopenharmony_ci * extensions always make such a nasty mess. 4318c2ecf20Sopenharmony_ci */ 4328c2ecf20Sopenharmony_ci if (dev->class >> 8 == PCI_CLASS_STORAGE_IDE && 4338c2ecf20Sopenharmony_ci (dev->class & 0x05) == 0) 4348c2ecf20Sopenharmony_ci return 0; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci if (entry) { 4388c2ecf20Sopenharmony_ci if (entry->link) 4398c2ecf20Sopenharmony_ci gsi = acpi_pci_link_allocate_irq(entry->link, 4408c2ecf20Sopenharmony_ci entry->index, 4418c2ecf20Sopenharmony_ci &triggering, &polarity, 4428c2ecf20Sopenharmony_ci &link); 4438c2ecf20Sopenharmony_ci else 4448c2ecf20Sopenharmony_ci gsi = entry->index; 4458c2ecf20Sopenharmony_ci } else 4468c2ecf20Sopenharmony_ci gsi = -1; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci if (gsi < 0) { 4498c2ecf20Sopenharmony_ci /* 4508c2ecf20Sopenharmony_ci * No IRQ known to the ACPI subsystem - maybe the BIOS / 4518c2ecf20Sopenharmony_ci * driver reported one, then use it. Exit in any case. 4528c2ecf20Sopenharmony_ci */ 4538c2ecf20Sopenharmony_ci if (!acpi_pci_irq_valid(dev, pin)) { 4548c2ecf20Sopenharmony_ci kfree(entry); 4558c2ecf20Sopenharmony_ci return 0; 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (acpi_isa_register_gsi(dev)) 4598c2ecf20Sopenharmony_ci dev_warn(&dev->dev, "PCI INT %c: no GSI\n", 4608c2ecf20Sopenharmony_ci pin_name(pin)); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci kfree(entry); 4638c2ecf20Sopenharmony_ci return 0; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci rc = acpi_register_gsi(&dev->dev, gsi, triggering, polarity); 4678c2ecf20Sopenharmony_ci if (rc < 0) { 4688c2ecf20Sopenharmony_ci dev_warn(&dev->dev, "PCI INT %c: failed to register GSI\n", 4698c2ecf20Sopenharmony_ci pin_name(pin)); 4708c2ecf20Sopenharmony_ci kfree(entry); 4718c2ecf20Sopenharmony_ci return rc; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci dev->irq = rc; 4748c2ecf20Sopenharmony_ci dev->irq_managed = 1; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci if (link) 4778c2ecf20Sopenharmony_ci snprintf(link_desc, sizeof(link_desc), " -> Link[%s]", link); 4788c2ecf20Sopenharmony_ci else 4798c2ecf20Sopenharmony_ci link_desc[0] = '\0'; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "PCI INT %c%s -> GSI %u (%s, %s) -> IRQ %d\n", 4828c2ecf20Sopenharmony_ci pin_name(pin), link_desc, gsi, 4838c2ecf20Sopenharmony_ci (triggering == ACPI_LEVEL_SENSITIVE) ? "level" : "edge", 4848c2ecf20Sopenharmony_ci (polarity == ACPI_ACTIVE_LOW) ? "low" : "high", dev->irq); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci kfree(entry); 4878c2ecf20Sopenharmony_ci return 0; 4888c2ecf20Sopenharmony_ci} 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_civoid acpi_pci_irq_disable(struct pci_dev *dev) 4918c2ecf20Sopenharmony_ci{ 4928c2ecf20Sopenharmony_ci struct acpi_prt_entry *entry; 4938c2ecf20Sopenharmony_ci int gsi; 4948c2ecf20Sopenharmony_ci u8 pin; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci pin = dev->pin; 4978c2ecf20Sopenharmony_ci if (!pin || !dev->irq_managed || dev->irq <= 0) 4988c2ecf20Sopenharmony_ci return; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci /* Keep IOAPIC pin configuration when suspending */ 5018c2ecf20Sopenharmony_ci if (dev->dev.power.is_prepared) 5028c2ecf20Sopenharmony_ci return; 5038c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 5048c2ecf20Sopenharmony_ci if (dev->dev.power.runtime_status == RPM_SUSPENDING) 5058c2ecf20Sopenharmony_ci return; 5068c2ecf20Sopenharmony_ci#endif 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci entry = acpi_pci_irq_lookup(dev, pin); 5098c2ecf20Sopenharmony_ci if (!entry) 5108c2ecf20Sopenharmony_ci return; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci if (entry->link) 5138c2ecf20Sopenharmony_ci gsi = acpi_pci_link_free_irq(entry->link); 5148c2ecf20Sopenharmony_ci else 5158c2ecf20Sopenharmony_ci gsi = entry->index; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci kfree(entry); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci /* 5208c2ecf20Sopenharmony_ci * TBD: It might be worth clearing dev->irq by magic constant 5218c2ecf20Sopenharmony_ci * (e.g. PCI_UNDEFINED_IRQ). 5228c2ecf20Sopenharmony_ci */ 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "PCI INT %c disabled\n", pin_name(pin)); 5258c2ecf20Sopenharmony_ci if (gsi >= 0) { 5268c2ecf20Sopenharmony_ci acpi_unregister_gsi(gsi); 5278c2ecf20Sopenharmony_ci dev->irq_managed = 0; 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci} 530