1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * pci_irq.c - ACPI PCI Interrupt Routing ($Revision: 11 $)
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
61da177e4SLinus Torvalds * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
71da177e4SLinus Torvalds * Copyright (C) 2002 Dominik Brodowski <devel@brodo.de>
839488b04SBjorn Helgaas * (c) Copyright 2008 Hewlett-Packard Development Company, L.P.
939488b04SBjorn Helgaas * Bjorn Helgaas <bjorn.helgaas@hp.com>
101da177e4SLinus Torvalds */
111da177e4SLinus Torvalds
12bf5144a6SRafael J. Wysocki #define pr_fmt(fmt) "ACPI: PCI: " fmt
131da177e4SLinus Torvalds
14391df5dcSBjorn Helgaas #include <linux/dmi.h>
151da177e4SLinus Torvalds #include <linux/kernel.h>
161da177e4SLinus Torvalds #include <linux/module.h>
171da177e4SLinus Torvalds #include <linux/init.h>
181da177e4SLinus Torvalds #include <linux/types.h>
191da177e4SLinus Torvalds #include <linux/spinlock.h>
201da177e4SLinus Torvalds #include <linux/pm.h>
211da177e4SLinus Torvalds #include <linux/pci.h>
221da177e4SLinus Torvalds #include <linux/acpi.h>
235a0e3ad6STejun Heo #include <linux/slab.h>
24e237a551SChen Fan #include <linux/interrupt.h>
251da177e4SLinus Torvalds
26f748bafaSBjorn Helgaas struct acpi_prt_entry {
27f748bafaSBjorn Helgaas struct acpi_pci_id id;
28f748bafaSBjorn Helgaas u8 pin;
294eaf6db3SBjorn Helgaas acpi_handle link;
304eaf6db3SBjorn Helgaas u32 index; /* GSI, or link _CRS index */
31f748bafaSBjorn Helgaas };
32f748bafaSBjorn Helgaas
pin_name(int pin)33cf68b80bSBjorn Helgaas static inline char pin_name(int pin)
34cf68b80bSBjorn Helgaas {
35e64e9db5SBjorn Helgaas return 'A' + pin - 1;
36cf68b80bSBjorn Helgaas }
37cf68b80bSBjorn Helgaas
381da177e4SLinus Torvalds /* --------------------------------------------------------------------------
391da177e4SLinus Torvalds PCI IRQ Routing Table (PRT) Support
401da177e4SLinus Torvalds -------------------------------------------------------------------------- */
411da177e4SLinus Torvalds
42391df5dcSBjorn Helgaas /* http://bugzilla.kernel.org/show_bug.cgi?id=4773 */
43609d4bc9SJan Beulich static const struct dmi_system_id medion_md9580[] = {
44391df5dcSBjorn Helgaas {
45391df5dcSBjorn Helgaas .ident = "Medion MD9580-F laptop",
46391df5dcSBjorn Helgaas .matches = {
47391df5dcSBjorn Helgaas DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"),
48391df5dcSBjorn Helgaas DMI_MATCH(DMI_PRODUCT_NAME, "A555"),
49391df5dcSBjorn Helgaas },
50391df5dcSBjorn Helgaas },
51391df5dcSBjorn Helgaas { }
52391df5dcSBjorn Helgaas };
53391df5dcSBjorn Helgaas
54391df5dcSBjorn Helgaas /* http://bugzilla.kernel.org/show_bug.cgi?id=5044 */
55609d4bc9SJan Beulich static const struct dmi_system_id dell_optiplex[] = {
56391df5dcSBjorn Helgaas {
57391df5dcSBjorn Helgaas .ident = "Dell Optiplex GX1",
58391df5dcSBjorn Helgaas .matches = {
59391df5dcSBjorn Helgaas DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
60391df5dcSBjorn Helgaas DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex GX1 600S+"),
61391df5dcSBjorn Helgaas },
62391df5dcSBjorn Helgaas },
63391df5dcSBjorn Helgaas { }
64391df5dcSBjorn Helgaas };
65391df5dcSBjorn Helgaas
66391df5dcSBjorn Helgaas /* http://bugzilla.kernel.org/show_bug.cgi?id=10138 */
67609d4bc9SJan Beulich static const struct dmi_system_id hp_t5710[] = {
68391df5dcSBjorn Helgaas {
69391df5dcSBjorn Helgaas .ident = "HP t5710",
70391df5dcSBjorn Helgaas .matches = {
71391df5dcSBjorn Helgaas DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
72391df5dcSBjorn Helgaas DMI_MATCH(DMI_PRODUCT_NAME, "hp t5000 series"),
73391df5dcSBjorn Helgaas DMI_MATCH(DMI_BOARD_NAME, "098Ch"),
74391df5dcSBjorn Helgaas },
75391df5dcSBjorn Helgaas },
76391df5dcSBjorn Helgaas { }
77391df5dcSBjorn Helgaas };
78391df5dcSBjorn Helgaas
79391df5dcSBjorn Helgaas struct prt_quirk {
80609d4bc9SJan Beulich const struct dmi_system_id *system;
81391df5dcSBjorn Helgaas unsigned int segment;
82391df5dcSBjorn Helgaas unsigned int bus;
83391df5dcSBjorn Helgaas unsigned int device;
84391df5dcSBjorn Helgaas unsigned char pin;
85609d4bc9SJan Beulich const char *source; /* according to BIOS */
86609d4bc9SJan Beulich const char *actual_source;
87391df5dcSBjorn Helgaas };
88391df5dcSBjorn Helgaas
89c458033cSBjorn Helgaas #define PCI_INTX_PIN(c) (c - 'A' + 1)
90c458033cSBjorn Helgaas
91391df5dcSBjorn Helgaas /*
92391df5dcSBjorn Helgaas * These systems have incorrect _PRT entries. The BIOS claims the PCI
93391df5dcSBjorn Helgaas * interrupt at the listed segment/bus/device/pin is connected to the first
94391df5dcSBjorn Helgaas * link device, but it is actually connected to the second.
95391df5dcSBjorn Helgaas */
96609d4bc9SJan Beulich static const struct prt_quirk prt_quirks[] = {
97c458033cSBjorn Helgaas { medion_md9580, 0, 0, 9, PCI_INTX_PIN('A'),
98b97d4803SBjorn Helgaas "\\_SB_.PCI0.ISA_.LNKA",
99b97d4803SBjorn Helgaas "\\_SB_.PCI0.ISA_.LNKB"},
100c458033cSBjorn Helgaas { dell_optiplex, 0, 0, 0xd, PCI_INTX_PIN('A'),
101391df5dcSBjorn Helgaas "\\_SB_.LNKB",
102391df5dcSBjorn Helgaas "\\_SB_.LNKA"},
103c458033cSBjorn Helgaas { hp_t5710, 0, 0, 1, PCI_INTX_PIN('A'),
104391df5dcSBjorn Helgaas "\\_SB_.PCI0.LNK1",
105391df5dcSBjorn Helgaas "\\_SB_.PCI0.LNK3"},
106391df5dcSBjorn Helgaas };
107391df5dcSBjorn Helgaas
do_prt_fixups(struct acpi_prt_entry * entry,struct acpi_pci_routing_table * prt)1083f0f3c27SBjorn Helgaas static void do_prt_fixups(struct acpi_prt_entry *entry,
1093f0f3c27SBjorn Helgaas struct acpi_pci_routing_table *prt)
110391df5dcSBjorn Helgaas {
111391df5dcSBjorn Helgaas int i;
112609d4bc9SJan Beulich const struct prt_quirk *quirk;
113391df5dcSBjorn Helgaas
114391df5dcSBjorn Helgaas for (i = 0; i < ARRAY_SIZE(prt_quirks); i++) {
115391df5dcSBjorn Helgaas quirk = &prt_quirks[i];
116391df5dcSBjorn Helgaas
117391df5dcSBjorn Helgaas /* All current quirks involve link devices, not GSIs */
118391df5dcSBjorn Helgaas if (dmi_check_system(quirk->system) &&
119391df5dcSBjorn Helgaas entry->id.segment == quirk->segment &&
120391df5dcSBjorn Helgaas entry->id.bus == quirk->bus &&
121391df5dcSBjorn Helgaas entry->id.device == quirk->device &&
122c458033cSBjorn Helgaas entry->pin == quirk->pin &&
123391df5dcSBjorn Helgaas !strcmp(prt->source, quirk->source) &&
124391df5dcSBjorn Helgaas strlen(prt->source) >= strlen(quirk->actual_source)) {
125bf5144a6SRafael J. Wysocki pr_warn("Firmware reports "
126c83642d5SBjorn Helgaas "%04x:%02x:%02x PCI INT %c connected to %s; "
127391df5dcSBjorn Helgaas "changing to %s\n",
128391df5dcSBjorn Helgaas entry->id.segment, entry->id.bus,
129cf68b80bSBjorn Helgaas entry->id.device, pin_name(entry->pin),
130391df5dcSBjorn Helgaas prt->source, quirk->actual_source);
131391df5dcSBjorn Helgaas strcpy(prt->source, quirk->actual_source);
132391df5dcSBjorn Helgaas }
133391df5dcSBjorn Helgaas }
134391df5dcSBjorn Helgaas }
135391df5dcSBjorn Helgaas
acpi_pci_irq_check_entry(acpi_handle handle,struct pci_dev * dev,int pin,struct acpi_pci_routing_table * prt,struct acpi_prt_entry ** entry_ptr)136181380b7SYinghai Lu static int acpi_pci_irq_check_entry(acpi_handle handle, struct pci_dev *dev,
137181380b7SYinghai Lu int pin, struct acpi_pci_routing_table *prt,
138181380b7SYinghai Lu struct acpi_prt_entry **entry_ptr)
1391da177e4SLinus Torvalds {
140181380b7SYinghai Lu int segment = pci_domain_nr(dev->bus);
141181380b7SYinghai Lu int bus = dev->bus->number;
142917bfd93SAlex Williamson int device = pci_ari_enabled(dev->bus) ? 0 : PCI_SLOT(dev->devfn);
1433604a9f4SBjorn Helgaas struct acpi_prt_entry *entry;
1441da177e4SLinus Torvalds
145181380b7SYinghai Lu if (((prt->address >> 16) & 0xffff) != device ||
146181380b7SYinghai Lu prt->pin + 1 != pin)
147181380b7SYinghai Lu return -ENODEV;
148181380b7SYinghai Lu
14936bcbec7SBurman Yan entry = kzalloc(sizeof(struct acpi_prt_entry), GFP_KERNEL);
1501da177e4SLinus Torvalds if (!entry)
151d550d98dSPatrick Mochel return -ENOMEM;
1521da177e4SLinus Torvalds
153e64e9db5SBjorn Helgaas /*
154e64e9db5SBjorn Helgaas * Note that the _PRT uses 0=INTA, 1=INTB, etc, while PCI uses
155e64e9db5SBjorn Helgaas * 1=INTA, 2=INTB. We use the PCI encoding throughout, so convert
156e64e9db5SBjorn Helgaas * it here.
157e64e9db5SBjorn Helgaas */
15879c44122SBjorn Helgaas entry->id.segment = segment;
15979c44122SBjorn Helgaas entry->id.bus = bus;
1601da177e4SLinus Torvalds entry->id.device = (prt->address >> 16) & 0xFFFF;
161e64e9db5SBjorn Helgaas entry->pin = prt->pin + 1;
1621da177e4SLinus Torvalds
163391df5dcSBjorn Helgaas do_prt_fixups(entry, prt);
164391df5dcSBjorn Helgaas
1654eaf6db3SBjorn Helgaas entry->index = prt->source_index;
1664eaf6db3SBjorn Helgaas
1671da177e4SLinus Torvalds /*
1681da177e4SLinus Torvalds * Type 1: Dynamic
1691da177e4SLinus Torvalds * ---------------
1701da177e4SLinus Torvalds * The 'source' field specifies the PCI interrupt link device used to
1711da177e4SLinus Torvalds * configure the IRQ assigned to this slot|dev|pin. The 'source_index'
1721da177e4SLinus Torvalds * indicates which resource descriptor in the resource template (of
1731da177e4SLinus Torvalds * the link device) this interrupt is allocated from.
1741da177e4SLinus Torvalds *
1751da177e4SLinus Torvalds * NOTE: Don't query the Link Device for IRQ information at this time
1761da177e4SLinus Torvalds * because Link Device enumeration may not have occurred yet
1771da177e4SLinus Torvalds * (e.g. exists somewhere 'below' this _PRT entry in the ACPI
1781da177e4SLinus Torvalds * namespace).
1791da177e4SLinus Torvalds */
1804eaf6db3SBjorn Helgaas if (prt->source[0])
1814eaf6db3SBjorn Helgaas acpi_get_handle(handle, prt->source, &entry->link);
1824eaf6db3SBjorn Helgaas
1831da177e4SLinus Torvalds /*
1841da177e4SLinus Torvalds * Type 2: Static
1851da177e4SLinus Torvalds * --------------
1861da177e4SLinus Torvalds * The 'source' field is NULL, and the 'source_index' field specifies
1871da177e4SLinus Torvalds * the IRQ value, which is hardwired to specific interrupt inputs on
1881da177e4SLinus Torvalds * the interrupt controller.
1891da177e4SLinus Torvalds */
190bf5144a6SRafael J. Wysocki pr_debug("%04x:%02x:%02x[%c] -> %s[%d]\n",
191bf5144a6SRafael J. Wysocki entry->id.segment, entry->id.bus, entry->id.device,
192bf5144a6SRafael J. Wysocki pin_name(entry->pin), prt->source, entry->index);
1931da177e4SLinus Torvalds
194181380b7SYinghai Lu *entry_ptr = entry;
1951da177e4SLinus Torvalds
196d550d98dSPatrick Mochel return 0;
1971da177e4SLinus Torvalds }
1981da177e4SLinus Torvalds
acpi_pci_irq_find_prt_entry(struct pci_dev * dev,int pin,struct acpi_prt_entry ** entry_ptr)199181380b7SYinghai Lu static int acpi_pci_irq_find_prt_entry(struct pci_dev *dev,
200181380b7SYinghai Lu int pin, struct acpi_prt_entry **entry_ptr)
2011da177e4SLinus Torvalds {
2022320ac6cSBjorn Helgaas acpi_status status;
2032320ac6cSBjorn Helgaas struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
2042320ac6cSBjorn Helgaas struct acpi_pci_routing_table *entry;
205181380b7SYinghai Lu acpi_handle handle = NULL;
2061da177e4SLinus Torvalds
207181380b7SYinghai Lu if (dev->bus->bridge)
208181380b7SYinghai Lu handle = ACPI_HANDLE(dev->bus->bridge);
209181380b7SYinghai Lu
210181380b7SYinghai Lu if (!handle)
2112320ac6cSBjorn Helgaas return -ENODEV;
2121da177e4SLinus Torvalds
213181380b7SYinghai Lu /* 'handle' is the _PRT's parent (root bridge or PCI-PCI bridge) */
2141da177e4SLinus Torvalds status = acpi_get_irq_routing_table(handle, &buffer);
2151da177e4SLinus Torvalds if (ACPI_FAILURE(status)) {
2161da177e4SLinus Torvalds kfree(buffer.pointer);
217d550d98dSPatrick Mochel return -ENODEV;
2181da177e4SLinus Torvalds }
2191da177e4SLinus Torvalds
2202320ac6cSBjorn Helgaas entry = buffer.pointer;
2211da177e4SLinus Torvalds while (entry && (entry->length > 0)) {
222181380b7SYinghai Lu if (!acpi_pci_irq_check_entry(handle, dev, pin,
223181380b7SYinghai Lu entry, entry_ptr))
224181380b7SYinghai Lu break;
2251da177e4SLinus Torvalds entry = (struct acpi_pci_routing_table *)
2261da177e4SLinus Torvalds ((unsigned long)entry + entry->length);
2271da177e4SLinus Torvalds }
2281da177e4SLinus Torvalds
2292320ac6cSBjorn Helgaas kfree(buffer.pointer);
230d550d98dSPatrick Mochel return 0;
2311da177e4SLinus Torvalds }
2321da177e4SLinus Torvalds
2331da177e4SLinus Torvalds /* --------------------------------------------------------------------------
2341da177e4SLinus Torvalds PCI Interrupt Routing Support
2351da177e4SLinus Torvalds -------------------------------------------------------------------------- */
236d7f6169aSStefan Assmann #ifdef CONFIG_X86_IO_APIC
237d7f6169aSStefan Assmann extern int noioapicquirk;
238d7f6169aSStefan Assmann extern int noioapicreroute;
239d7f6169aSStefan Assmann
bridge_has_boot_interrupt_variant(struct pci_bus * bus)240d7f6169aSStefan Assmann static int bridge_has_boot_interrupt_variant(struct pci_bus *bus)
241d7f6169aSStefan Assmann {
242d7f6169aSStefan Assmann struct pci_bus *bus_it;
243d7f6169aSStefan Assmann
244d7f6169aSStefan Assmann for (bus_it = bus ; bus_it ; bus_it = bus_it->parent) {
245d7f6169aSStefan Assmann if (!bus_it->self)
246d7f6169aSStefan Assmann return 0;
247d7f6169aSStefan Assmann if (bus_it->self->irq_reroute_variant)
248d7f6169aSStefan Assmann return bus_it->self->irq_reroute_variant;
249d7f6169aSStefan Assmann }
250d7f6169aSStefan Assmann return 0;
251d7f6169aSStefan Assmann }
252d7f6169aSStefan Assmann
253d7f6169aSStefan Assmann /*
254d7f6169aSStefan Assmann * Some chipsets (e.g. Intel 6700PXH) generate a legacy INTx when the IRQ
255d7f6169aSStefan Assmann * entry in the chipset's IO-APIC is masked (as, e.g. the RT kernel does
256d7f6169aSStefan Assmann * during interrupt handling). When this INTx generation cannot be disabled,
257d7f6169aSStefan Assmann * we reroute these interrupts to their legacy equivalent to get rid of
258d7f6169aSStefan Assmann * spurious interrupts.
259d7f6169aSStefan Assmann */
acpi_reroute_boot_interrupt(struct pci_dev * dev,struct acpi_prt_entry * entry)260d7f6169aSStefan Assmann static int acpi_reroute_boot_interrupt(struct pci_dev *dev,
261d7f6169aSStefan Assmann struct acpi_prt_entry *entry)
262d7f6169aSStefan Assmann {
263d7f6169aSStefan Assmann if (noioapicquirk || noioapicreroute) {
264d7f6169aSStefan Assmann return 0;
265d7f6169aSStefan Assmann } else {
266d7f6169aSStefan Assmann switch (bridge_has_boot_interrupt_variant(dev->bus)) {
267d7f6169aSStefan Assmann case 0:
268d7f6169aSStefan Assmann /* no rerouting necessary */
269d7f6169aSStefan Assmann return 0;
270d7f6169aSStefan Assmann case INTEL_IRQ_REROUTE_VARIANT:
271d7f6169aSStefan Assmann /*
272d7f6169aSStefan Assmann * Remap according to INTx routing table in 6700PXH
273d7f6169aSStefan Assmann * specs, intel order number 302628-002, section
274d7f6169aSStefan Assmann * 2.15.2. Other chipsets (80332, ...) have the same
275d7f6169aSStefan Assmann * mapping and are handled here as well.
276d7f6169aSStefan Assmann */
277d7f6169aSStefan Assmann dev_info(&dev->dev, "PCI IRQ %d -> rerouted to legacy "
278d7f6169aSStefan Assmann "IRQ %d\n", entry->index,
279d7f6169aSStefan Assmann (entry->index % 4) + 16);
280d7f6169aSStefan Assmann entry->index = (entry->index % 4) + 16;
281d7f6169aSStefan Assmann return 1;
282d7f6169aSStefan Assmann default:
283d7f6169aSStefan Assmann dev_warn(&dev->dev, "Cannot reroute IRQ %d to legacy "
284d7f6169aSStefan Assmann "IRQ: unknown mapping\n", entry->index);
285d7f6169aSStefan Assmann return -1;
286d7f6169aSStefan Assmann }
287d7f6169aSStefan Assmann }
288d7f6169aSStefan Assmann }
289d7f6169aSStefan Assmann #endif /* CONFIG_X86_IO_APIC */
290d7f6169aSStefan Assmann
acpi_pci_irq_lookup(struct pci_dev * dev,int pin)2913f0f3c27SBjorn Helgaas static struct acpi_prt_entry *acpi_pci_irq_lookup(struct pci_dev *dev, int pin)
2921da177e4SLinus Torvalds {
293181380b7SYinghai Lu struct acpi_prt_entry *entry = NULL;
2945697b7caSBjorn Helgaas struct pci_dev *bridge;
2955697b7caSBjorn Helgaas u8 bridge_pin, orig_pin = pin;
296181380b7SYinghai Lu int ret;
2971da177e4SLinus Torvalds
298181380b7SYinghai Lu ret = acpi_pci_irq_find_prt_entry(dev, pin, &entry);
299181380b7SYinghai Lu if (!ret && entry) {
300d7f6169aSStefan Assmann #ifdef CONFIG_X86_IO_APIC
301d7f6169aSStefan Assmann acpi_reroute_boot_interrupt(dev, entry);
302d7f6169aSStefan Assmann #endif /* CONFIG_X86_IO_APIC */
303bf5144a6SRafael J. Wysocki dev_dbg(&dev->dev, "Found [%c] _PRT entry\n", pin_name(pin));
3043b1ea18dSBjorn Helgaas return entry;
3053b1ea18dSBjorn Helgaas }
3063b1ea18dSBjorn Helgaas
3071da177e4SLinus Torvalds /*
3081da177e4SLinus Torvalds * Attempt to derive an IRQ for this device from a parent bridge's
3091da177e4SLinus Torvalds * PCI interrupt routing entry (eg. yenta bridge and add-in card bridge).
3101da177e4SLinus Torvalds */
311ee401363SBjorn Helgaas bridge = dev->bus->self;
312ee401363SBjorn Helgaas while (bridge) {
313c686d141SBjorn Helgaas pin = pci_swizzle_interrupt_pin(dev, pin);
3141da177e4SLinus Torvalds
3151da177e4SLinus Torvalds if ((bridge->class >> 8) == PCI_CLASS_BRIDGE_CARDBUS) {
3161da177e4SLinus Torvalds /* PC card has the same IRQ as its cardbridge */
3178015a014SKristen Accardi bridge_pin = bridge->pin;
3181da177e4SLinus Torvalds if (!bridge_pin) {
319bf5144a6SRafael J. Wysocki dev_dbg(&bridge->dev, "No interrupt pin configured\n");
320beba8a64SBjorn Helgaas return NULL;
3211da177e4SLinus Torvalds }
3221da177e4SLinus Torvalds pin = bridge_pin;
3231da177e4SLinus Torvalds }
3241da177e4SLinus Torvalds
325181380b7SYinghai Lu ret = acpi_pci_irq_find_prt_entry(bridge, pin, &entry);
326181380b7SYinghai Lu if (!ret && entry) {
327bf5144a6SRafael J. Wysocki dev_dbg(&dev->dev, "Derived GSI INT %c from %s\n",
328bf5144a6SRafael J. Wysocki pin_name(orig_pin), pci_name(bridge));
3293b1ea18dSBjorn Helgaas return entry;
3303b1ea18dSBjorn Helgaas }
331ee401363SBjorn Helgaas
332ee401363SBjorn Helgaas dev = bridge;
333ee401363SBjorn Helgaas bridge = dev->bus->self;
3341da177e4SLinus Torvalds }
3351da177e4SLinus Torvalds
336c83642d5SBjorn Helgaas dev_warn(&dev->dev, "can't derive routing for PCI INT %c\n",
337cf68b80bSBjorn Helgaas pin_name(orig_pin));
338beba8a64SBjorn Helgaas return NULL;
3391da177e4SLinus Torvalds }
3401da177e4SLinus Torvalds
341c1aaae67STomasz Nowicki #if IS_ENABLED(CONFIG_ISA) || IS_ENABLED(CONFIG_EISA)
acpi_isa_register_gsi(struct pci_dev * dev)342c1aaae67STomasz Nowicki static int acpi_isa_register_gsi(struct pci_dev *dev)
343c1aaae67STomasz Nowicki {
344c1aaae67STomasz Nowicki u32 dev_gsi;
345c1aaae67STomasz Nowicki
346c1aaae67STomasz Nowicki /* Interrupt Line values above 0xF are forbidden */
347c1aaae67STomasz Nowicki if (dev->irq > 0 && (dev->irq <= 0xF) &&
3485ebc7603SJiang Liu acpi_isa_irq_available(dev->irq) &&
349c1aaae67STomasz Nowicki (acpi_isa_irq_to_gsi(dev->irq, &dev_gsi) == 0)) {
350c1aaae67STomasz Nowicki dev_warn(&dev->dev, "PCI INT %c: no GSI - using ISA IRQ %d\n",
351c1aaae67STomasz Nowicki pin_name(dev->pin), dev->irq);
352c1aaae67STomasz Nowicki acpi_register_gsi(&dev->dev, dev_gsi,
353c1aaae67STomasz Nowicki ACPI_LEVEL_SENSITIVE,
354c1aaae67STomasz Nowicki ACPI_ACTIVE_LOW);
355c1aaae67STomasz Nowicki return 0;
356c1aaae67STomasz Nowicki }
357c1aaae67STomasz Nowicki return -EINVAL;
358c1aaae67STomasz Nowicki }
359c1aaae67STomasz Nowicki #else
acpi_isa_register_gsi(struct pci_dev * dev)360c1aaae67STomasz Nowicki static inline int acpi_isa_register_gsi(struct pci_dev *dev)
361c1aaae67STomasz Nowicki {
362c1aaae67STomasz Nowicki return -ENODEV;
363c1aaae67STomasz Nowicki }
364c1aaae67STomasz Nowicki #endif
365c1aaae67STomasz Nowicki
acpi_pci_irq_valid(struct pci_dev * dev,u8 pin)366e237a551SChen Fan static inline bool acpi_pci_irq_valid(struct pci_dev *dev, u8 pin)
367e237a551SChen Fan {
368e237a551SChen Fan #ifdef CONFIG_X86
369e237a551SChen Fan /*
370e237a551SChen Fan * On x86 irq line 0xff means "unknown" or "no connection"
371e237a551SChen Fan * (PCI 3.0, Section 6.2.4, footnote on page 223).
372e237a551SChen Fan */
373e237a551SChen Fan if (dev->irq == 0xff) {
374e237a551SChen Fan dev->irq = IRQ_NOTCONNECTED;
375e237a551SChen Fan dev_warn(&dev->dev, "PCI INT %c: not connected\n",
376e237a551SChen Fan pin_name(pin));
377e237a551SChen Fan return false;
378e237a551SChen Fan }
379e237a551SChen Fan #endif
380e237a551SChen Fan return true;
381e237a551SChen Fan }
382e237a551SChen Fan
acpi_pci_irq_enable(struct pci_dev * dev)3834be44fcdSLen Brown int acpi_pci_irq_enable(struct pci_dev *dev)
3841da177e4SLinus Torvalds {
385beba8a64SBjorn Helgaas struct acpi_prt_entry *entry;
3863f0f3c27SBjorn Helgaas int gsi;
3873f0f3c27SBjorn Helgaas u8 pin;
38850eca3ebSBob Moore int triggering = ACPI_LEVEL_SENSITIVE;
38910b68700SLorenzo Pieralisi /*
390*d0c50cc4SJianmin Lv * On ARM systems with the GIC interrupt model, or LoongArch
391*d0c50cc4SJianmin Lv * systems with the LPIC interrupt model, level interrupts
39210b68700SLorenzo Pieralisi * are always polarity high by specification; PCI legacy
39310b68700SLorenzo Pieralisi * IRQs lines are inverted before reaching the interrupt
39410b68700SLorenzo Pieralisi * controller and must therefore be considered active high
39510b68700SLorenzo Pieralisi * as default.
39610b68700SLorenzo Pieralisi */
397*d0c50cc4SJianmin Lv int polarity = acpi_irq_model == ACPI_IRQ_MODEL_GIC ||
398*d0c50cc4SJianmin Lv acpi_irq_model == ACPI_IRQ_MODEL_LPIC ?
39910b68700SLorenzo Pieralisi ACPI_ACTIVE_HIGH : ACPI_ACTIVE_LOW;
4001da177e4SLinus Torvalds char *link = NULL;
401c83642d5SBjorn Helgaas char link_desc[16];
402349f0d56SKenji Kaneshige int rc;
4031da177e4SLinus Torvalds
4048015a014SKristen Accardi pin = dev->pin;
4051da177e4SLinus Torvalds if (!pin) {
406bf5144a6SRafael J. Wysocki dev_dbg(&dev->dev, "No interrupt pin configured\n");
407d550d98dSPatrick Mochel return 0;
4081da177e4SLinus Torvalds }
4091da177e4SLinus Torvalds
41067b4eab9SBjorn Helgaas if (dev->irq_managed && dev->irq > 0)
411cffe0a2bSJiang Liu return 0;
412cffe0a2bSJiang Liu
413beba8a64SBjorn Helgaas entry = acpi_pci_irq_lookup(dev, pin);
4145697b7caSBjorn Helgaas if (!entry) {
41596c2a876SAlan Cox /*
41696c2a876SAlan Cox * IDE legacy mode controller IRQs are magic. Why do compat
41796c2a876SAlan Cox * extensions always make such a nasty mess.
41896c2a876SAlan Cox */
41996c2a876SAlan Cox if (dev->class >> 8 == PCI_CLASS_STORAGE_IDE &&
42096c2a876SAlan Cox (dev->class & 0x05) == 0)
42196c2a876SAlan Cox return 0;
42296c2a876SAlan Cox }
4235697b7caSBjorn Helgaas
42474f82af1SBjorn Helgaas if (entry) {
42574f82af1SBjorn Helgaas if (entry->link)
42674f82af1SBjorn Helgaas gsi = acpi_pci_link_allocate_irq(entry->link,
42774f82af1SBjorn Helgaas entry->index,
42874f82af1SBjorn Helgaas &triggering, &polarity,
4295697b7caSBjorn Helgaas &link);
4305697b7caSBjorn Helgaas else
43174f82af1SBjorn Helgaas gsi = entry->index;
43274f82af1SBjorn Helgaas } else
4335697b7caSBjorn Helgaas gsi = -1;
4345697b7caSBjorn Helgaas
435e237a551SChen Fan if (gsi < 0) {
4361da177e4SLinus Torvalds /*
4371da177e4SLinus Torvalds * No IRQ known to the ACPI subsystem - maybe the BIOS /
4381da177e4SLinus Torvalds * driver reported one, then use it. Exit in any case.
4391da177e4SLinus Torvalds */
44029b49958SWenwen Wang if (!acpi_pci_irq_valid(dev, pin)) {
44129b49958SWenwen Wang kfree(entry);
442e237a551SChen Fan return 0;
44329b49958SWenwen Wang }
444e237a551SChen Fan
445c1aaae67STomasz Nowicki if (acpi_isa_register_gsi(dev))
44666fd3835SJoe Perches dev_warn(&dev->dev, "PCI INT %c: no GSI\n",
44766fd3835SJoe Perches pin_name(pin));
448181380b7SYinghai Lu
449b685f3b1STomasz Nowicki kfree(entry);
45066fd3835SJoe Perches return 0;
4511da177e4SLinus Torvalds }
4521da177e4SLinus Torvalds
453a2f809b0SYinghai Lu rc = acpi_register_gsi(&dev->dev, gsi, triggering, polarity);
454349f0d56SKenji Kaneshige if (rc < 0) {
455c83642d5SBjorn Helgaas dev_warn(&dev->dev, "PCI INT %c: failed to register GSI\n",
456cf68b80bSBjorn Helgaas pin_name(pin));
457181380b7SYinghai Lu kfree(entry);
458d550d98dSPatrick Mochel return rc;
459349f0d56SKenji Kaneshige }
46067b4eab9SBjorn Helgaas dev->irq = rc;
46167b4eab9SBjorn Helgaas dev->irq_managed = 1;
4621da177e4SLinus Torvalds
4631da177e4SLinus Torvalds if (link)
464c83642d5SBjorn Helgaas snprintf(link_desc, sizeof(link_desc), " -> Link[%s]", link);
465c83642d5SBjorn Helgaas else
466c83642d5SBjorn Helgaas link_desc[0] = '\0';
4671da177e4SLinus Torvalds
46885b8582dSVincent Palatin dev_dbg(&dev->dev, "PCI INT %c%s -> GSI %u (%s, %s) -> IRQ %d\n",
469cf68b80bSBjorn Helgaas pin_name(pin), link_desc, gsi,
47050eca3ebSBob Moore (triggering == ACPI_LEVEL_SENSITIVE) ? "level" : "edge",
47150eca3ebSBob Moore (polarity == ACPI_ACTIVE_LOW) ? "low" : "high", dev->irq);
4721da177e4SLinus Torvalds
473181380b7SYinghai Lu kfree(entry);
474d550d98dSPatrick Mochel return 0;
4751da177e4SLinus Torvalds }
4764be44fcdSLen Brown
acpi_pci_irq_disable(struct pci_dev * dev)4774be44fcdSLen Brown void acpi_pci_irq_disable(struct pci_dev *dev)
4781da177e4SLinus Torvalds {
479beba8a64SBjorn Helgaas struct acpi_prt_entry *entry;
4803f0f3c27SBjorn Helgaas int gsi;
4813f0f3c27SBjorn Helgaas u8 pin;
4821da177e4SLinus Torvalds
4838015a014SKristen Accardi pin = dev->pin;
48467b4eab9SBjorn Helgaas if (!pin || !dev->irq_managed || dev->irq <= 0)
485d550d98dSPatrick Mochel return;
4861da177e4SLinus Torvalds
4876c777e87SBjorn Helgaas /* Keep IOAPIC pin configuration when suspending */
4886c777e87SBjorn Helgaas if (dev->dev.power.is_prepared)
4896c777e87SBjorn Helgaas return;
4906c777e87SBjorn Helgaas #ifdef CONFIG_PM
4916c777e87SBjorn Helgaas if (dev->dev.power.runtime_status == RPM_SUSPENDING)
4926c777e87SBjorn Helgaas return;
4936c777e87SBjorn Helgaas #endif
4946c777e87SBjorn Helgaas
495beba8a64SBjorn Helgaas entry = acpi_pci_irq_lookup(dev, pin);
496beba8a64SBjorn Helgaas if (!entry)
497d550d98dSPatrick Mochel return;
4981da177e4SLinus Torvalds
49974f82af1SBjorn Helgaas if (entry->link)
50074f82af1SBjorn Helgaas gsi = acpi_pci_link_free_irq(entry->link);
50174f82af1SBjorn Helgaas else
50274f82af1SBjorn Helgaas gsi = entry->index;
503beba8a64SBjorn Helgaas
504181380b7SYinghai Lu kfree(entry);
505181380b7SYinghai Lu
5061da177e4SLinus Torvalds /*
5071da177e4SLinus Torvalds * TBD: It might be worth clearing dev->irq by magic constant
5081da177e4SLinus Torvalds * (e.g. PCI_UNDEFINED_IRQ).
5091da177e4SLinus Torvalds */
5101da177e4SLinus Torvalds
51185b8582dSVincent Palatin dev_dbg(&dev->dev, "PCI INT %c disabled\n", pin_name(pin));
512cffe0a2bSJiang Liu if (gsi >= 0) {
5131da177e4SLinus Torvalds acpi_unregister_gsi(gsi);
51467b4eab9SBjorn Helgaas dev->irq_managed = 0;
515cffe0a2bSJiang Liu }
5161da177e4SLinus Torvalds }
517