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