xref: /openbmc/linux/drivers/acpi/pci_irq.c (revision 7ae9fb1b7ecbb5d85d07857943f677fd1a559b18)
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