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