linux/drivers/pci/irq.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * PCI IRQ handling code
   4 *
   5 * Copyright (c) 2008 James Bottomley <James.Bottomley@HansenPartnership.com>
   6 * Copyright (C) 2017 Christoph Hellwig.
   7 */
   8
   9#include <linux/acpi.h>
  10#include <linux/device.h>
  11#include <linux/kernel.h>
  12#include <linux/export.h>
  13#include <linux/pci.h>
  14
  15static void pci_note_irq_problem(struct pci_dev *pdev, const char *reason)
  16{
  17        struct pci_dev *parent = to_pci_dev(pdev->dev.parent);
  18
  19        pci_err(pdev, "Potentially misrouted IRQ (Bridge %s %04x:%04x)\n",
  20                dev_name(&parent->dev), parent->vendor, parent->device);
  21        pci_err(pdev, "%s\n", reason);
  22        pci_err(pdev, "Please report to linux-kernel@vger.kernel.org\n");
  23        WARN_ON(1);
  24}
  25
  26/**
  27 * pci_lost_interrupt - reports a lost PCI interrupt
  28 * @pdev:       device whose interrupt is lost
  29 *
  30 * The primary function of this routine is to report a lost interrupt
  31 * in a standard way which users can recognise (instead of blaming the
  32 * driver).
  33 *
  34 * Returns:
  35 * a suggestion for fixing it (although the driver is not required to
  36 * act on this).
  37 */
  38enum pci_lost_interrupt_reason pci_lost_interrupt(struct pci_dev *pdev)
  39{
  40        if (pdev->msi_enabled || pdev->msix_enabled) {
  41                enum pci_lost_interrupt_reason ret;
  42
  43                if (pdev->msix_enabled) {
  44                        pci_note_irq_problem(pdev, "MSIX routing failure");
  45                        ret = PCI_LOST_IRQ_DISABLE_MSIX;
  46                } else {
  47                        pci_note_irq_problem(pdev, "MSI routing failure");
  48                        ret = PCI_LOST_IRQ_DISABLE_MSI;
  49                }
  50                return ret;
  51        }
  52#ifdef CONFIG_ACPI
  53        if (!(acpi_disabled || acpi_noirq)) {
  54                pci_note_irq_problem(pdev, "Potential ACPI misrouting please reboot with acpi=noirq");
  55                /* currently no way to fix acpi on the fly */
  56                return PCI_LOST_IRQ_DISABLE_ACPI;
  57        }
  58#endif
  59        pci_note_irq_problem(pdev, "unknown cause (not MSI or ACPI)");
  60        return PCI_LOST_IRQ_NO_INFORMATION;
  61}
  62EXPORT_SYMBOL(pci_lost_interrupt);
  63
  64/**
  65 * pci_request_irq - allocate an interrupt line for a PCI device
  66 * @dev:        PCI device to operate on
  67 * @nr:         device-relative interrupt vector index (0-based).
  68 * @handler:    Function to be called when the IRQ occurs.
  69 *              Primary handler for threaded interrupts.
  70 *              If NULL and thread_fn != NULL the default primary handler is
  71 *              installed.
  72 * @thread_fn:  Function called from the IRQ handler thread
  73 *              If NULL, no IRQ thread is created
  74 * @dev_id:     Cookie passed back to the handler function
  75 * @fmt:        Printf-like format string naming the handler
  76 *
  77 * This call allocates interrupt resources and enables the interrupt line and
  78 * IRQ handling. From the point this call is made @handler and @thread_fn may
  79 * be invoked.  All interrupts requested using this function might be shared.
  80 *
  81 * @dev_id must not be NULL and must be globally unique.
  82 */
  83int pci_request_irq(struct pci_dev *dev, unsigned int nr, irq_handler_t handler,
  84                irq_handler_t thread_fn, void *dev_id, const char *fmt, ...)
  85{
  86        va_list ap;
  87        int ret;
  88        char *devname;
  89        unsigned long irqflags = IRQF_SHARED;
  90
  91        if (!handler)
  92                irqflags |= IRQF_ONESHOT;
  93
  94        va_start(ap, fmt);
  95        devname = kvasprintf(GFP_KERNEL, fmt, ap);
  96        va_end(ap);
  97
  98        ret = request_threaded_irq(pci_irq_vector(dev, nr), handler, thread_fn,
  99                                   irqflags, devname, dev_id);
 100        if (ret)
 101                kfree(devname);
 102        return ret;
 103}
 104EXPORT_SYMBOL(pci_request_irq);
 105
 106/**
 107 * pci_free_irq - free an interrupt allocated with pci_request_irq
 108 * @dev:        PCI device to operate on
 109 * @nr:         device-relative interrupt vector index (0-based).
 110 * @dev_id:     Device identity to free
 111 *
 112 * Remove an interrupt handler. The handler is removed and if the interrupt
 113 * line is no longer in use by any driver it is disabled.  The caller must
 114 * ensure the interrupt is disabled on the device before calling this function.
 115 * The function does not return until any executing interrupts for this IRQ
 116 * have completed.
 117 *
 118 * This function must not be called from interrupt context.
 119 */
 120void pci_free_irq(struct pci_dev *dev, unsigned int nr, void *dev_id)
 121{
 122        kfree(free_irq(pci_irq_vector(dev, nr), dev_id));
 123}
 124EXPORT_SYMBOL(pci_free_irq);
 125