summaryrefslogtreecommitdiff
path: root/drivers/acpi/pci_irq.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/acpi/pci_irq.c')
-rw-r--r--drivers/acpi/pci_irq.c168
1 files changed, 98 insertions, 70 deletions
diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c
index 41c5e1b799ef..ad81aa03fe2f 100644
--- a/drivers/acpi/pci_irq.c
+++ b/drivers/acpi/pci_irq.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* pci_irq.c - ACPI PCI Interrupt Routing ($Revision: 11 $)
*
@@ -6,26 +7,9 @@
* Copyright (C) 2002 Dominik Brodowski <devel@brodo.de>
* (c) Copyright 2008 Hewlett-Packard Development Company, L.P.
* Bjorn Helgaas <bjorn.helgaas@hp.com>
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at
- * your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
+#define pr_fmt(fmt) "ACPI: PCI: " fmt
#include <linux/dmi.h>
#include <linux/kernel.h>
@@ -37,16 +21,10 @@
#include <linux/pci.h>
#include <linux/acpi.h>
#include <linux/slab.h>
-#include <acpi/acpi_bus.h>
-#include <acpi/acpi_drivers.h>
-
-#define PREFIX "ACPI: "
-
-#define _COMPONENT ACPI_PCI_COMPONENT
-ACPI_MODULE_NAME("pci_irq");
+#include <linux/interrupt.h>
+#include <linux/string_choices.h>
struct acpi_prt_entry {
- struct list_head list;
struct acpi_pci_id id;
u8 pin;
acpi_handle link;
@@ -138,9 +116,6 @@ static void do_prt_fixups(struct acpi_prt_entry *entry,
quirk = &prt_quirks[i];
/* All current quirks involve link devices, not GSIs */
- if (!prt->source)
- continue;
-
if (dmi_check_system(quirk->system) &&
entry->id.segment == quirk->segment &&
entry->id.bus == quirk->bus &&
@@ -148,7 +123,7 @@ static void do_prt_fixups(struct acpi_prt_entry *entry,
entry->pin == quirk->pin &&
!strcmp(prt->source, quirk->source) &&
strlen(prt->source) >= strlen(quirk->actual_source)) {
- printk(KERN_WARNING PREFIX "firmware reports "
+ pr_warn("Firmware reports "
"%04x:%02x:%02x PCI INT %c connected to %s; "
"changing to %s\n",
entry->id.segment, entry->id.bus,
@@ -165,7 +140,7 @@ static int acpi_pci_irq_check_entry(acpi_handle handle, struct pci_dev *dev,
{
int segment = pci_domain_nr(dev->bus);
int bus = dev->bus->number;
- int device = PCI_SLOT(dev->devfn);
+ int device = pci_ari_enabled(dev->bus) ? 0 : PCI_SLOT(dev->devfn);
struct acpi_prt_entry *entry;
if (((prt->address >> 16) & 0xffff) != device ||
@@ -197,7 +172,7 @@ static int acpi_pci_irq_check_entry(acpi_handle handle, struct pci_dev *dev,
* configure the IRQ assigned to this slot|dev|pin. The 'source_index'
* indicates which resource descriptor in the resource template (of
* the link device) this interrupt is allocated from.
- *
+ *
* NOTE: Don't query the Link Device for IRQ information at this time
* because Link Device enumeration may not have occurred yet
* (e.g. exists somewhere 'below' this _PRT entry in the ACPI
@@ -213,12 +188,9 @@ static int acpi_pci_irq_check_entry(acpi_handle handle, struct pci_dev *dev,
* the IRQ value, which is hardwired to specific interrupt inputs on
* the interrupt controller.
*/
-
- ACPI_DEBUG_PRINT_RAW((ACPI_DB_INFO,
- " %04x:%02x:%02x[%c] -> %s[%d]\n",
- entry->id.segment, entry->id.bus,
- entry->id.device, pin_name(entry->pin),
- prt->source, entry->index));
+ pr_debug("%04x:%02x:%02x[%c] -> %s[%d]\n",
+ entry->id.segment, entry->id.bus, entry->id.device,
+ pin_name(entry->pin), prt->source, entry->index);
*entry_ptr = entry;
@@ -317,7 +289,7 @@ static int acpi_reroute_boot_interrupt(struct pci_dev *dev,
}
#endif /* CONFIG_X86_IO_APIC */
-static struct acpi_prt_entry *acpi_pci_irq_lookup(struct pci_dev *dev, int pin)
+struct acpi_prt_entry *acpi_pci_irq_lookup(struct pci_dev *dev, int pin)
{
struct acpi_prt_entry *entry = NULL;
struct pci_dev *bridge;
@@ -329,8 +301,7 @@ static struct acpi_prt_entry *acpi_pci_irq_lookup(struct pci_dev *dev, int pin)
#ifdef CONFIG_X86_IO_APIC
acpi_reroute_boot_interrupt(dev, entry);
#endif /* CONFIG_X86_IO_APIC */
- ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %s[%c] _PRT entry\n",
- pci_name(dev), pin_name(pin)));
+ dev_dbg(&dev->dev, "Found [%c] _PRT entry\n", pin_name(pin));
return entry;
}
@@ -346,9 +317,7 @@ static struct acpi_prt_entry *acpi_pci_irq_lookup(struct pci_dev *dev, int pin)
/* PC card has the same IRQ as its cardbridge */
bridge_pin = bridge->pin;
if (!bridge_pin) {
- ACPI_DEBUG_PRINT((ACPI_DB_INFO,
- "No interrupt pin configured for device %s\n",
- pci_name(bridge)));
+ dev_dbg(&bridge->dev, "No interrupt pin configured\n");
return NULL;
}
pin = bridge_pin;
@@ -356,10 +325,8 @@ static struct acpi_prt_entry *acpi_pci_irq_lookup(struct pci_dev *dev, int pin)
ret = acpi_pci_irq_find_prt_entry(bridge, pin, &entry);
if (!ret && entry) {
- ACPI_DEBUG_PRINT((ACPI_DB_INFO,
- "Derived GSI for %s INT %c from %s\n",
- pci_name(dev), pin_name(orig_pin),
- pci_name(bridge)));
+ dev_dbg(&dev->dev, "Derived GSI INT %c from %s\n",
+ pin_name(orig_pin), pci_name(bridge));
return entry;
}
@@ -372,25 +339,78 @@ static struct acpi_prt_entry *acpi_pci_irq_lookup(struct pci_dev *dev, int pin)
return NULL;
}
+#if IS_ENABLED(CONFIG_ISA) || IS_ENABLED(CONFIG_EISA)
+static int acpi_isa_register_gsi(struct pci_dev *dev)
+{
+ u32 dev_gsi;
+
+ /* Interrupt Line values above 0xF are forbidden */
+ if (dev->irq > 0 && (dev->irq <= 0xF) &&
+ acpi_isa_irq_available(dev->irq) &&
+ (acpi_isa_irq_to_gsi(dev->irq, &dev_gsi) == 0)) {
+ dev_warn(&dev->dev, "PCI INT %c: no GSI - using ISA IRQ %d\n",
+ pin_name(dev->pin), dev->irq);
+ acpi_register_gsi(&dev->dev, dev_gsi,
+ ACPI_LEVEL_SENSITIVE,
+ ACPI_ACTIVE_LOW);
+ return 0;
+ }
+ return -EINVAL;
+}
+#else
+static inline int acpi_isa_register_gsi(struct pci_dev *dev)
+{
+ return -ENODEV;
+}
+#endif
+
+static inline bool acpi_pci_irq_valid(struct pci_dev *dev, u8 pin)
+{
+#ifdef CONFIG_X86
+ /*
+ * On x86 irq line 0xff means "unknown" or "no connection"
+ * (PCI 3.0, Section 6.2.4, footnote on page 223).
+ */
+ if (dev->irq == 0xff) {
+ dev->irq = IRQ_NOTCONNECTED;
+ dev_warn(&dev->dev, "PCI INT %c: not connected\n",
+ pin_name(pin));
+ return false;
+ }
+#endif
+ return true;
+}
+
int acpi_pci_irq_enable(struct pci_dev *dev)
{
struct acpi_prt_entry *entry;
int gsi;
u8 pin;
int triggering = ACPI_LEVEL_SENSITIVE;
- int polarity = ACPI_ACTIVE_LOW;
+ /*
+ * On ARM systems with the GIC interrupt model, or LoongArch
+ * systems with the LPIC interrupt model, level interrupts
+ * are always polarity high by specification; PCI legacy
+ * IRQs lines are inverted before reaching the interrupt
+ * controller and must therefore be considered active high
+ * as default.
+ */
+ int polarity = acpi_irq_model == ACPI_IRQ_MODEL_GIC ||
+ acpi_irq_model == ACPI_IRQ_MODEL_LPIC ?
+ ACPI_ACTIVE_HIGH : ACPI_ACTIVE_LOW;
char *link = NULL;
char link_desc[16];
int rc;
pin = dev->pin;
if (!pin) {
- ACPI_DEBUG_PRINT((ACPI_DB_INFO,
- "No interrupt pin configured for device %s\n",
- pci_name(dev)));
+ dev_dbg(&dev->dev, "No interrupt pin configured\n");
return 0;
}
+ if (dev->irq_managed && dev->irq > 0)
+ return 0;
+
entry = acpi_pci_irq_lookup(dev, pin);
if (!entry) {
/*
@@ -413,25 +433,21 @@ int acpi_pci_irq_enable(struct pci_dev *dev)
} else
gsi = -1;
- /*
- * No IRQ known to the ACPI subsystem - maybe the BIOS /
- * driver reported one, then use it. Exit in any case.
- */
if (gsi < 0) {
- u32 dev_gsi;
- /* Interrupt Line values above 0xF are forbidden */
- if (dev->irq > 0 && (dev->irq <= 0xF) &&
- (acpi_isa_irq_to_gsi(dev->irq, &dev_gsi) == 0)) {
- dev_warn(&dev->dev, "PCI INT %c: no GSI - using ISA IRQ %d\n",
- pin_name(pin), dev->irq);
- acpi_register_gsi(&dev->dev, dev_gsi,
- ACPI_LEVEL_SENSITIVE,
- ACPI_ACTIVE_LOW);
- } else {
+ /*
+ * No IRQ known to the ACPI subsystem - maybe the BIOS /
+ * driver reported one, then use it. Exit in any case.
+ */
+ if (!acpi_pci_irq_valid(dev, pin)) {
+ kfree(entry);
+ return 0;
+ }
+
+ if (acpi_isa_register_gsi(dev))
dev_warn(&dev->dev, "PCI INT %c: no GSI\n",
pin_name(pin));
- }
+ kfree(entry);
return 0;
}
@@ -443,6 +459,7 @@ int acpi_pci_irq_enable(struct pci_dev *dev)
return rc;
}
dev->irq = rc;
+ dev->irq_managed = 1;
if (link)
snprintf(link_desc, sizeof(link_desc), " -> Link[%s]", link);
@@ -452,7 +469,7 @@ int acpi_pci_irq_enable(struct pci_dev *dev)
dev_dbg(&dev->dev, "PCI INT %c%s -> GSI %u (%s, %s) -> IRQ %d\n",
pin_name(pin), link_desc, gsi,
(triggering == ACPI_LEVEL_SENSITIVE) ? "level" : "edge",
- (polarity == ACPI_ACTIVE_LOW) ? "low" : "high", dev->irq);
+ str_low_high(polarity == ACPI_ACTIVE_LOW), dev->irq);
kfree(entry);
return 0;
@@ -465,9 +482,17 @@ void acpi_pci_irq_disable(struct pci_dev *dev)
u8 pin;
pin = dev->pin;
- if (!pin)
+ if (!pin || !dev->irq_managed || dev->irq <= 0)
return;
+ /* Keep IOAPIC pin configuration when suspending */
+ if (dev->dev.power.is_prepared)
+ return;
+#ifdef CONFIG_PM
+ if (dev->dev.power.runtime_status == RPM_SUSPENDING)
+ return;
+#endif
+
entry = acpi_pci_irq_lookup(dev, pin);
if (!entry)
return;
@@ -485,5 +510,8 @@ void acpi_pci_irq_disable(struct pci_dev *dev)
*/
dev_dbg(&dev->dev, "PCI INT %c disabled\n", pin_name(pin));
- acpi_unregister_gsi(gsi);
+ if (gsi >= 0) {
+ acpi_unregister_gsi(gsi);
+ dev->irq_managed = 0;
+ }
}