162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * pci_irq.c - ACPI PCI Interrupt Routing ($Revision: 11 $) 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> 662306a36Sopenharmony_ci * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> 762306a36Sopenharmony_ci * Copyright (C) 2002 Dominik Brodowski <devel@brodo.de> 862306a36Sopenharmony_ci * (c) Copyright 2008 Hewlett-Packard Development Company, L.P. 962306a36Sopenharmony_ci * Bjorn Helgaas <bjorn.helgaas@hp.com> 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#define pr_fmt(fmt) "ACPI: PCI: " fmt 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/dmi.h> 1562306a36Sopenharmony_ci#include <linux/kernel.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/init.h> 1862306a36Sopenharmony_ci#include <linux/types.h> 1962306a36Sopenharmony_ci#include <linux/spinlock.h> 2062306a36Sopenharmony_ci#include <linux/pm.h> 2162306a36Sopenharmony_ci#include <linux/pci.h> 2262306a36Sopenharmony_ci#include <linux/acpi.h> 2362306a36Sopenharmony_ci#include <linux/slab.h> 2462306a36Sopenharmony_ci#include <linux/interrupt.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistruct acpi_prt_entry { 2762306a36Sopenharmony_ci struct acpi_pci_id id; 2862306a36Sopenharmony_ci u8 pin; 2962306a36Sopenharmony_ci acpi_handle link; 3062306a36Sopenharmony_ci u32 index; /* GSI, or link _CRS index */ 3162306a36Sopenharmony_ci}; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic inline char pin_name(int pin) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci return 'A' + pin - 1; 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* -------------------------------------------------------------------------- 3962306a36Sopenharmony_ci PCI IRQ Routing Table (PRT) Support 4062306a36Sopenharmony_ci -------------------------------------------------------------------------- */ 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* http://bugzilla.kernel.org/show_bug.cgi?id=4773 */ 4362306a36Sopenharmony_cistatic const struct dmi_system_id medion_md9580[] = { 4462306a36Sopenharmony_ci { 4562306a36Sopenharmony_ci .ident = "Medion MD9580-F laptop", 4662306a36Sopenharmony_ci .matches = { 4762306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"), 4862306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "A555"), 4962306a36Sopenharmony_ci }, 5062306a36Sopenharmony_ci }, 5162306a36Sopenharmony_ci { } 5262306a36Sopenharmony_ci}; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* http://bugzilla.kernel.org/show_bug.cgi?id=5044 */ 5562306a36Sopenharmony_cistatic const struct dmi_system_id dell_optiplex[] = { 5662306a36Sopenharmony_ci { 5762306a36Sopenharmony_ci .ident = "Dell Optiplex GX1", 5862306a36Sopenharmony_ci .matches = { 5962306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), 6062306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex GX1 600S+"), 6162306a36Sopenharmony_ci }, 6262306a36Sopenharmony_ci }, 6362306a36Sopenharmony_ci { } 6462306a36Sopenharmony_ci}; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* http://bugzilla.kernel.org/show_bug.cgi?id=10138 */ 6762306a36Sopenharmony_cistatic const struct dmi_system_id hp_t5710[] = { 6862306a36Sopenharmony_ci { 6962306a36Sopenharmony_ci .ident = "HP t5710", 7062306a36Sopenharmony_ci .matches = { 7162306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), 7262306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "hp t5000 series"), 7362306a36Sopenharmony_ci DMI_MATCH(DMI_BOARD_NAME, "098Ch"), 7462306a36Sopenharmony_ci }, 7562306a36Sopenharmony_ci }, 7662306a36Sopenharmony_ci { } 7762306a36Sopenharmony_ci}; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistruct prt_quirk { 8062306a36Sopenharmony_ci const struct dmi_system_id *system; 8162306a36Sopenharmony_ci unsigned int segment; 8262306a36Sopenharmony_ci unsigned int bus; 8362306a36Sopenharmony_ci unsigned int device; 8462306a36Sopenharmony_ci unsigned char pin; 8562306a36Sopenharmony_ci const char *source; /* according to BIOS */ 8662306a36Sopenharmony_ci const char *actual_source; 8762306a36Sopenharmony_ci}; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci#define PCI_INTX_PIN(c) (c - 'A' + 1) 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/* 9262306a36Sopenharmony_ci * These systems have incorrect _PRT entries. The BIOS claims the PCI 9362306a36Sopenharmony_ci * interrupt at the listed segment/bus/device/pin is connected to the first 9462306a36Sopenharmony_ci * link device, but it is actually connected to the second. 9562306a36Sopenharmony_ci */ 9662306a36Sopenharmony_cistatic const struct prt_quirk prt_quirks[] = { 9762306a36Sopenharmony_ci { medion_md9580, 0, 0, 9, PCI_INTX_PIN('A'), 9862306a36Sopenharmony_ci "\\_SB_.PCI0.ISA_.LNKA", 9962306a36Sopenharmony_ci "\\_SB_.PCI0.ISA_.LNKB"}, 10062306a36Sopenharmony_ci { dell_optiplex, 0, 0, 0xd, PCI_INTX_PIN('A'), 10162306a36Sopenharmony_ci "\\_SB_.LNKB", 10262306a36Sopenharmony_ci "\\_SB_.LNKA"}, 10362306a36Sopenharmony_ci { hp_t5710, 0, 0, 1, PCI_INTX_PIN('A'), 10462306a36Sopenharmony_ci "\\_SB_.PCI0.LNK1", 10562306a36Sopenharmony_ci "\\_SB_.PCI0.LNK3"}, 10662306a36Sopenharmony_ci}; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic void do_prt_fixups(struct acpi_prt_entry *entry, 10962306a36Sopenharmony_ci struct acpi_pci_routing_table *prt) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci int i; 11262306a36Sopenharmony_ci const struct prt_quirk *quirk; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(prt_quirks); i++) { 11562306a36Sopenharmony_ci quirk = &prt_quirks[i]; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci /* All current quirks involve link devices, not GSIs */ 11862306a36Sopenharmony_ci if (dmi_check_system(quirk->system) && 11962306a36Sopenharmony_ci entry->id.segment == quirk->segment && 12062306a36Sopenharmony_ci entry->id.bus == quirk->bus && 12162306a36Sopenharmony_ci entry->id.device == quirk->device && 12262306a36Sopenharmony_ci entry->pin == quirk->pin && 12362306a36Sopenharmony_ci !strcmp(prt->source, quirk->source) && 12462306a36Sopenharmony_ci strlen(prt->source) >= strlen(quirk->actual_source)) { 12562306a36Sopenharmony_ci pr_warn("Firmware reports " 12662306a36Sopenharmony_ci "%04x:%02x:%02x PCI INT %c connected to %s; " 12762306a36Sopenharmony_ci "changing to %s\n", 12862306a36Sopenharmony_ci entry->id.segment, entry->id.bus, 12962306a36Sopenharmony_ci entry->id.device, pin_name(entry->pin), 13062306a36Sopenharmony_ci prt->source, quirk->actual_source); 13162306a36Sopenharmony_ci strcpy(prt->source, quirk->actual_source); 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic int acpi_pci_irq_check_entry(acpi_handle handle, struct pci_dev *dev, 13762306a36Sopenharmony_ci int pin, struct acpi_pci_routing_table *prt, 13862306a36Sopenharmony_ci struct acpi_prt_entry **entry_ptr) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci int segment = pci_domain_nr(dev->bus); 14162306a36Sopenharmony_ci int bus = dev->bus->number; 14262306a36Sopenharmony_ci int device = pci_ari_enabled(dev->bus) ? 0 : PCI_SLOT(dev->devfn); 14362306a36Sopenharmony_ci struct acpi_prt_entry *entry; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (((prt->address >> 16) & 0xffff) != device || 14662306a36Sopenharmony_ci prt->pin + 1 != pin) 14762306a36Sopenharmony_ci return -ENODEV; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci entry = kzalloc(sizeof(struct acpi_prt_entry), GFP_KERNEL); 15062306a36Sopenharmony_ci if (!entry) 15162306a36Sopenharmony_ci return -ENOMEM; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci /* 15462306a36Sopenharmony_ci * Note that the _PRT uses 0=INTA, 1=INTB, etc, while PCI uses 15562306a36Sopenharmony_ci * 1=INTA, 2=INTB. We use the PCI encoding throughout, so convert 15662306a36Sopenharmony_ci * it here. 15762306a36Sopenharmony_ci */ 15862306a36Sopenharmony_ci entry->id.segment = segment; 15962306a36Sopenharmony_ci entry->id.bus = bus; 16062306a36Sopenharmony_ci entry->id.device = (prt->address >> 16) & 0xFFFF; 16162306a36Sopenharmony_ci entry->pin = prt->pin + 1; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci do_prt_fixups(entry, prt); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci entry->index = prt->source_index; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci /* 16862306a36Sopenharmony_ci * Type 1: Dynamic 16962306a36Sopenharmony_ci * --------------- 17062306a36Sopenharmony_ci * The 'source' field specifies the PCI interrupt link device used to 17162306a36Sopenharmony_ci * configure the IRQ assigned to this slot|dev|pin. The 'source_index' 17262306a36Sopenharmony_ci * indicates which resource descriptor in the resource template (of 17362306a36Sopenharmony_ci * the link device) this interrupt is allocated from. 17462306a36Sopenharmony_ci * 17562306a36Sopenharmony_ci * NOTE: Don't query the Link Device for IRQ information at this time 17662306a36Sopenharmony_ci * because Link Device enumeration may not have occurred yet 17762306a36Sopenharmony_ci * (e.g. exists somewhere 'below' this _PRT entry in the ACPI 17862306a36Sopenharmony_ci * namespace). 17962306a36Sopenharmony_ci */ 18062306a36Sopenharmony_ci if (prt->source[0]) 18162306a36Sopenharmony_ci acpi_get_handle(handle, prt->source, &entry->link); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* 18462306a36Sopenharmony_ci * Type 2: Static 18562306a36Sopenharmony_ci * -------------- 18662306a36Sopenharmony_ci * The 'source' field is NULL, and the 'source_index' field specifies 18762306a36Sopenharmony_ci * the IRQ value, which is hardwired to specific interrupt inputs on 18862306a36Sopenharmony_ci * the interrupt controller. 18962306a36Sopenharmony_ci */ 19062306a36Sopenharmony_ci pr_debug("%04x:%02x:%02x[%c] -> %s[%d]\n", 19162306a36Sopenharmony_ci entry->id.segment, entry->id.bus, entry->id.device, 19262306a36Sopenharmony_ci pin_name(entry->pin), prt->source, entry->index); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci *entry_ptr = entry; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci return 0; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic int acpi_pci_irq_find_prt_entry(struct pci_dev *dev, 20062306a36Sopenharmony_ci int pin, struct acpi_prt_entry **entry_ptr) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci acpi_status status; 20362306a36Sopenharmony_ci struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 20462306a36Sopenharmony_ci struct acpi_pci_routing_table *entry; 20562306a36Sopenharmony_ci acpi_handle handle = NULL; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (dev->bus->bridge) 20862306a36Sopenharmony_ci handle = ACPI_HANDLE(dev->bus->bridge); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (!handle) 21162306a36Sopenharmony_ci return -ENODEV; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci /* 'handle' is the _PRT's parent (root bridge or PCI-PCI bridge) */ 21462306a36Sopenharmony_ci status = acpi_get_irq_routing_table(handle, &buffer); 21562306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 21662306a36Sopenharmony_ci kfree(buffer.pointer); 21762306a36Sopenharmony_ci return -ENODEV; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci entry = buffer.pointer; 22162306a36Sopenharmony_ci while (entry && (entry->length > 0)) { 22262306a36Sopenharmony_ci if (!acpi_pci_irq_check_entry(handle, dev, pin, 22362306a36Sopenharmony_ci entry, entry_ptr)) 22462306a36Sopenharmony_ci break; 22562306a36Sopenharmony_ci entry = (struct acpi_pci_routing_table *) 22662306a36Sopenharmony_ci ((unsigned long)entry + entry->length); 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci kfree(buffer.pointer); 23062306a36Sopenharmony_ci return 0; 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci/* -------------------------------------------------------------------------- 23462306a36Sopenharmony_ci PCI Interrupt Routing Support 23562306a36Sopenharmony_ci -------------------------------------------------------------------------- */ 23662306a36Sopenharmony_ci#ifdef CONFIG_X86_IO_APIC 23762306a36Sopenharmony_ciextern int noioapicquirk; 23862306a36Sopenharmony_ciextern int noioapicreroute; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic int bridge_has_boot_interrupt_variant(struct pci_bus *bus) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci struct pci_bus *bus_it; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci for (bus_it = bus ; bus_it ; bus_it = bus_it->parent) { 24562306a36Sopenharmony_ci if (!bus_it->self) 24662306a36Sopenharmony_ci return 0; 24762306a36Sopenharmony_ci if (bus_it->self->irq_reroute_variant) 24862306a36Sopenharmony_ci return bus_it->self->irq_reroute_variant; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci return 0; 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci/* 25462306a36Sopenharmony_ci * Some chipsets (e.g. Intel 6700PXH) generate a legacy INTx when the IRQ 25562306a36Sopenharmony_ci * entry in the chipset's IO-APIC is masked (as, e.g. the RT kernel does 25662306a36Sopenharmony_ci * during interrupt handling). When this INTx generation cannot be disabled, 25762306a36Sopenharmony_ci * we reroute these interrupts to their legacy equivalent to get rid of 25862306a36Sopenharmony_ci * spurious interrupts. 25962306a36Sopenharmony_ci */ 26062306a36Sopenharmony_cistatic int acpi_reroute_boot_interrupt(struct pci_dev *dev, 26162306a36Sopenharmony_ci struct acpi_prt_entry *entry) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci if (noioapicquirk || noioapicreroute) { 26462306a36Sopenharmony_ci return 0; 26562306a36Sopenharmony_ci } else { 26662306a36Sopenharmony_ci switch (bridge_has_boot_interrupt_variant(dev->bus)) { 26762306a36Sopenharmony_ci case 0: 26862306a36Sopenharmony_ci /* no rerouting necessary */ 26962306a36Sopenharmony_ci return 0; 27062306a36Sopenharmony_ci case INTEL_IRQ_REROUTE_VARIANT: 27162306a36Sopenharmony_ci /* 27262306a36Sopenharmony_ci * Remap according to INTx routing table in 6700PXH 27362306a36Sopenharmony_ci * specs, intel order number 302628-002, section 27462306a36Sopenharmony_ci * 2.15.2. Other chipsets (80332, ...) have the same 27562306a36Sopenharmony_ci * mapping and are handled here as well. 27662306a36Sopenharmony_ci */ 27762306a36Sopenharmony_ci dev_info(&dev->dev, "PCI IRQ %d -> rerouted to legacy " 27862306a36Sopenharmony_ci "IRQ %d\n", entry->index, 27962306a36Sopenharmony_ci (entry->index % 4) + 16); 28062306a36Sopenharmony_ci entry->index = (entry->index % 4) + 16; 28162306a36Sopenharmony_ci return 1; 28262306a36Sopenharmony_ci default: 28362306a36Sopenharmony_ci dev_warn(&dev->dev, "Cannot reroute IRQ %d to legacy " 28462306a36Sopenharmony_ci "IRQ: unknown mapping\n", entry->index); 28562306a36Sopenharmony_ci return -1; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci#endif /* CONFIG_X86_IO_APIC */ 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic struct acpi_prt_entry *acpi_pci_irq_lookup(struct pci_dev *dev, int pin) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci struct acpi_prt_entry *entry = NULL; 29462306a36Sopenharmony_ci struct pci_dev *bridge; 29562306a36Sopenharmony_ci u8 bridge_pin, orig_pin = pin; 29662306a36Sopenharmony_ci int ret; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci ret = acpi_pci_irq_find_prt_entry(dev, pin, &entry); 29962306a36Sopenharmony_ci if (!ret && entry) { 30062306a36Sopenharmony_ci#ifdef CONFIG_X86_IO_APIC 30162306a36Sopenharmony_ci acpi_reroute_boot_interrupt(dev, entry); 30262306a36Sopenharmony_ci#endif /* CONFIG_X86_IO_APIC */ 30362306a36Sopenharmony_ci dev_dbg(&dev->dev, "Found [%c] _PRT entry\n", pin_name(pin)); 30462306a36Sopenharmony_ci return entry; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci /* 30862306a36Sopenharmony_ci * Attempt to derive an IRQ for this device from a parent bridge's 30962306a36Sopenharmony_ci * PCI interrupt routing entry (eg. yenta bridge and add-in card bridge). 31062306a36Sopenharmony_ci */ 31162306a36Sopenharmony_ci bridge = dev->bus->self; 31262306a36Sopenharmony_ci while (bridge) { 31362306a36Sopenharmony_ci pin = pci_swizzle_interrupt_pin(dev, pin); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if ((bridge->class >> 8) == PCI_CLASS_BRIDGE_CARDBUS) { 31662306a36Sopenharmony_ci /* PC card has the same IRQ as its cardbridge */ 31762306a36Sopenharmony_ci bridge_pin = bridge->pin; 31862306a36Sopenharmony_ci if (!bridge_pin) { 31962306a36Sopenharmony_ci dev_dbg(&bridge->dev, "No interrupt pin configured\n"); 32062306a36Sopenharmony_ci return NULL; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci pin = bridge_pin; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci ret = acpi_pci_irq_find_prt_entry(bridge, pin, &entry); 32662306a36Sopenharmony_ci if (!ret && entry) { 32762306a36Sopenharmony_ci dev_dbg(&dev->dev, "Derived GSI INT %c from %s\n", 32862306a36Sopenharmony_ci pin_name(orig_pin), pci_name(bridge)); 32962306a36Sopenharmony_ci return entry; 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci dev = bridge; 33362306a36Sopenharmony_ci bridge = dev->bus->self; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci dev_warn(&dev->dev, "can't derive routing for PCI INT %c\n", 33762306a36Sopenharmony_ci pin_name(orig_pin)); 33862306a36Sopenharmony_ci return NULL; 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_ISA) || IS_ENABLED(CONFIG_EISA) 34262306a36Sopenharmony_cistatic int acpi_isa_register_gsi(struct pci_dev *dev) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci u32 dev_gsi; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci /* Interrupt Line values above 0xF are forbidden */ 34762306a36Sopenharmony_ci if (dev->irq > 0 && (dev->irq <= 0xF) && 34862306a36Sopenharmony_ci acpi_isa_irq_available(dev->irq) && 34962306a36Sopenharmony_ci (acpi_isa_irq_to_gsi(dev->irq, &dev_gsi) == 0)) { 35062306a36Sopenharmony_ci dev_warn(&dev->dev, "PCI INT %c: no GSI - using ISA IRQ %d\n", 35162306a36Sopenharmony_ci pin_name(dev->pin), dev->irq); 35262306a36Sopenharmony_ci acpi_register_gsi(&dev->dev, dev_gsi, 35362306a36Sopenharmony_ci ACPI_LEVEL_SENSITIVE, 35462306a36Sopenharmony_ci ACPI_ACTIVE_LOW); 35562306a36Sopenharmony_ci return 0; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci return -EINVAL; 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci#else 36062306a36Sopenharmony_cistatic inline int acpi_isa_register_gsi(struct pci_dev *dev) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci return -ENODEV; 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci#endif 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic inline bool acpi_pci_irq_valid(struct pci_dev *dev, u8 pin) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci#ifdef CONFIG_X86 36962306a36Sopenharmony_ci /* 37062306a36Sopenharmony_ci * On x86 irq line 0xff means "unknown" or "no connection" 37162306a36Sopenharmony_ci * (PCI 3.0, Section 6.2.4, footnote on page 223). 37262306a36Sopenharmony_ci */ 37362306a36Sopenharmony_ci if (dev->irq == 0xff) { 37462306a36Sopenharmony_ci dev->irq = IRQ_NOTCONNECTED; 37562306a36Sopenharmony_ci dev_warn(&dev->dev, "PCI INT %c: not connected\n", 37662306a36Sopenharmony_ci pin_name(pin)); 37762306a36Sopenharmony_ci return false; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci#endif 38062306a36Sopenharmony_ci return true; 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ciint acpi_pci_irq_enable(struct pci_dev *dev) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci struct acpi_prt_entry *entry; 38662306a36Sopenharmony_ci int gsi; 38762306a36Sopenharmony_ci u8 pin; 38862306a36Sopenharmony_ci int triggering = ACPI_LEVEL_SENSITIVE; 38962306a36Sopenharmony_ci /* 39062306a36Sopenharmony_ci * On ARM systems with the GIC interrupt model, or LoongArch 39162306a36Sopenharmony_ci * systems with the LPIC interrupt model, level interrupts 39262306a36Sopenharmony_ci * are always polarity high by specification; PCI legacy 39362306a36Sopenharmony_ci * IRQs lines are inverted before reaching the interrupt 39462306a36Sopenharmony_ci * controller and must therefore be considered active high 39562306a36Sopenharmony_ci * as default. 39662306a36Sopenharmony_ci */ 39762306a36Sopenharmony_ci int polarity = acpi_irq_model == ACPI_IRQ_MODEL_GIC || 39862306a36Sopenharmony_ci acpi_irq_model == ACPI_IRQ_MODEL_LPIC ? 39962306a36Sopenharmony_ci ACPI_ACTIVE_HIGH : ACPI_ACTIVE_LOW; 40062306a36Sopenharmony_ci char *link = NULL; 40162306a36Sopenharmony_ci char link_desc[16]; 40262306a36Sopenharmony_ci int rc; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci pin = dev->pin; 40562306a36Sopenharmony_ci if (!pin) { 40662306a36Sopenharmony_ci dev_dbg(&dev->dev, "No interrupt pin configured\n"); 40762306a36Sopenharmony_ci return 0; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci if (dev->irq_managed && dev->irq > 0) 41162306a36Sopenharmony_ci return 0; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci entry = acpi_pci_irq_lookup(dev, pin); 41462306a36Sopenharmony_ci if (!entry) { 41562306a36Sopenharmony_ci /* 41662306a36Sopenharmony_ci * IDE legacy mode controller IRQs are magic. Why do compat 41762306a36Sopenharmony_ci * extensions always make such a nasty mess. 41862306a36Sopenharmony_ci */ 41962306a36Sopenharmony_ci if (dev->class >> 8 == PCI_CLASS_STORAGE_IDE && 42062306a36Sopenharmony_ci (dev->class & 0x05) == 0) 42162306a36Sopenharmony_ci return 0; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci if (entry) { 42562306a36Sopenharmony_ci if (entry->link) 42662306a36Sopenharmony_ci gsi = acpi_pci_link_allocate_irq(entry->link, 42762306a36Sopenharmony_ci entry->index, 42862306a36Sopenharmony_ci &triggering, &polarity, 42962306a36Sopenharmony_ci &link); 43062306a36Sopenharmony_ci else 43162306a36Sopenharmony_ci gsi = entry->index; 43262306a36Sopenharmony_ci } else 43362306a36Sopenharmony_ci gsi = -1; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (gsi < 0) { 43662306a36Sopenharmony_ci /* 43762306a36Sopenharmony_ci * No IRQ known to the ACPI subsystem - maybe the BIOS / 43862306a36Sopenharmony_ci * driver reported one, then use it. Exit in any case. 43962306a36Sopenharmony_ci */ 44062306a36Sopenharmony_ci if (!acpi_pci_irq_valid(dev, pin)) { 44162306a36Sopenharmony_ci kfree(entry); 44262306a36Sopenharmony_ci return 0; 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci if (acpi_isa_register_gsi(dev)) 44662306a36Sopenharmony_ci dev_warn(&dev->dev, "PCI INT %c: no GSI\n", 44762306a36Sopenharmony_ci pin_name(pin)); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci kfree(entry); 45062306a36Sopenharmony_ci return 0; 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci rc = acpi_register_gsi(&dev->dev, gsi, triggering, polarity); 45462306a36Sopenharmony_ci if (rc < 0) { 45562306a36Sopenharmony_ci dev_warn(&dev->dev, "PCI INT %c: failed to register GSI\n", 45662306a36Sopenharmony_ci pin_name(pin)); 45762306a36Sopenharmony_ci kfree(entry); 45862306a36Sopenharmony_ci return rc; 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci dev->irq = rc; 46162306a36Sopenharmony_ci dev->irq_managed = 1; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if (link) 46462306a36Sopenharmony_ci snprintf(link_desc, sizeof(link_desc), " -> Link[%s]", link); 46562306a36Sopenharmony_ci else 46662306a36Sopenharmony_ci link_desc[0] = '\0'; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci dev_dbg(&dev->dev, "PCI INT %c%s -> GSI %u (%s, %s) -> IRQ %d\n", 46962306a36Sopenharmony_ci pin_name(pin), link_desc, gsi, 47062306a36Sopenharmony_ci (triggering == ACPI_LEVEL_SENSITIVE) ? "level" : "edge", 47162306a36Sopenharmony_ci (polarity == ACPI_ACTIVE_LOW) ? "low" : "high", dev->irq); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci kfree(entry); 47462306a36Sopenharmony_ci return 0; 47562306a36Sopenharmony_ci} 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_civoid acpi_pci_irq_disable(struct pci_dev *dev) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci struct acpi_prt_entry *entry; 48062306a36Sopenharmony_ci int gsi; 48162306a36Sopenharmony_ci u8 pin; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci pin = dev->pin; 48462306a36Sopenharmony_ci if (!pin || !dev->irq_managed || dev->irq <= 0) 48562306a36Sopenharmony_ci return; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci /* Keep IOAPIC pin configuration when suspending */ 48862306a36Sopenharmony_ci if (dev->dev.power.is_prepared) 48962306a36Sopenharmony_ci return; 49062306a36Sopenharmony_ci#ifdef CONFIG_PM 49162306a36Sopenharmony_ci if (dev->dev.power.runtime_status == RPM_SUSPENDING) 49262306a36Sopenharmony_ci return; 49362306a36Sopenharmony_ci#endif 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci entry = acpi_pci_irq_lookup(dev, pin); 49662306a36Sopenharmony_ci if (!entry) 49762306a36Sopenharmony_ci return; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (entry->link) 50062306a36Sopenharmony_ci gsi = acpi_pci_link_free_irq(entry->link); 50162306a36Sopenharmony_ci else 50262306a36Sopenharmony_ci gsi = entry->index; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci kfree(entry); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci /* 50762306a36Sopenharmony_ci * TBD: It might be worth clearing dev->irq by magic constant 50862306a36Sopenharmony_ci * (e.g. PCI_UNDEFINED_IRQ). 50962306a36Sopenharmony_ci */ 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci dev_dbg(&dev->dev, "PCI INT %c disabled\n", pin_name(pin)); 51262306a36Sopenharmony_ci if (gsi >= 0) { 51362306a36Sopenharmony_ci acpi_unregister_gsi(gsi); 51462306a36Sopenharmony_ci dev->irq_managed = 0; 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci} 517