You've already forked linux-rockchip
mirror of
https://github.com/armbian/linux-rockchip.git
synced 2026-01-06 11:08:10 -08:00
PCI: Provide sensible IRQ vector alloc/free routines
Add a function to allocate and free a range of interrupt vectors, using MSI-X, MSI or legacy vectors (in that order) based on the capabilities of the underlying device and PCIe complex. Additionally a new helper is provided to get the Linux IRQ number for given device-relative vector so that the drivers don't need to allocate their own arrays to keep track of the vectors for the multi vector MSI-X case. Signed-off-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Reviewed-by: Alexander Gordeev <agordeev@redhat.com>
This commit is contained in:
committed by
Bjorn Helgaas
parent
3ac020e0ca
commit
aff171641d
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,7 @@
|
||||
*
|
||||
* Copyright (C) 2003-2004 Intel
|
||||
* Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
|
||||
* Copyright (C) 2016 Christoph Hellwig.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
@@ -1121,6 +1122,94 @@ int pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries,
|
||||
}
|
||||
EXPORT_SYMBOL(pci_enable_msix_range);
|
||||
|
||||
/**
|
||||
* pci_alloc_irq_vectors - allocate multiple IRQs for a device
|
||||
* @dev: PCI device to operate on
|
||||
* @min_vecs: minimum number of vectors required (must be >= 1)
|
||||
* @max_vecs: maximum (desired) number of vectors
|
||||
* @flags: flags or quirks for the allocation
|
||||
*
|
||||
* Allocate up to @max_vecs interrupt vectors for @dev, using MSI-X or MSI
|
||||
* vectors if available, and fall back to a single legacy vector
|
||||
* if neither is available. Return the number of vectors allocated,
|
||||
* (which might be smaller than @max_vecs) if successful, or a negative
|
||||
* error code on error. If less than @min_vecs interrupt vectors are
|
||||
* available for @dev the function will fail with -ENOSPC.
|
||||
*
|
||||
* To get the Linux IRQ number used for a vector that can be passed to
|
||||
* request_irq() use the pci_irq_vector() helper.
|
||||
*/
|
||||
int pci_alloc_irq_vectors(struct pci_dev *dev, unsigned int min_vecs,
|
||||
unsigned int max_vecs, unsigned int flags)
|
||||
{
|
||||
int vecs = -ENOSPC;
|
||||
|
||||
if (!(flags & PCI_IRQ_NOMSIX)) {
|
||||
vecs = pci_enable_msix_range(dev, NULL, min_vecs, max_vecs);
|
||||
if (vecs > 0)
|
||||
return vecs;
|
||||
}
|
||||
|
||||
if (!(flags & PCI_IRQ_NOMSI)) {
|
||||
vecs = pci_enable_msi_range(dev, min_vecs, max_vecs);
|
||||
if (vecs > 0)
|
||||
return vecs;
|
||||
}
|
||||
|
||||
/* use legacy irq if allowed */
|
||||
if (!(flags & PCI_IRQ_NOLEGACY) && min_vecs == 1)
|
||||
return 1;
|
||||
return vecs;
|
||||
}
|
||||
EXPORT_SYMBOL(pci_alloc_irq_vectors);
|
||||
|
||||
/**
|
||||
* pci_free_irq_vectors - free previously allocated IRQs for a device
|
||||
* @dev: PCI device to operate on
|
||||
*
|
||||
* Undoes the allocations and enabling in pci_alloc_irq_vectors().
|
||||
*/
|
||||
void pci_free_irq_vectors(struct pci_dev *dev)
|
||||
{
|
||||
pci_disable_msix(dev);
|
||||
pci_disable_msi(dev);
|
||||
}
|
||||
EXPORT_SYMBOL(pci_free_irq_vectors);
|
||||
|
||||
/**
|
||||
* pci_irq_vector - return Linux IRQ number of a device vector
|
||||
* @dev: PCI device to operate on
|
||||
* @nr: device-relative interrupt vector index (0-based).
|
||||
*/
|
||||
int pci_irq_vector(struct pci_dev *dev, unsigned int nr)
|
||||
{
|
||||
if (dev->msix_enabled) {
|
||||
struct msi_desc *entry;
|
||||
int i = 0;
|
||||
|
||||
for_each_pci_msi_entry(entry, dev) {
|
||||
if (i == nr)
|
||||
return entry->irq;
|
||||
i++;
|
||||
}
|
||||
WARN_ON_ONCE(1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (dev->msi_enabled) {
|
||||
struct msi_desc *entry = first_pci_msi_entry(dev);
|
||||
|
||||
if (WARN_ON_ONCE(nr >= entry->nvec_used))
|
||||
return -EINVAL;
|
||||
} else {
|
||||
if (WARN_ON_ONCE(nr > 0))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return dev->irq + nr;
|
||||
}
|
||||
EXPORT_SYMBOL(pci_irq_vector);
|
||||
|
||||
struct pci_dev *msi_desc_to_pci_dev(struct msi_desc *desc)
|
||||
{
|
||||
return to_pci_dev(desc->dev);
|
||||
|
||||
@@ -1237,6 +1237,10 @@ resource_size_t pcibios_iov_resource_alignment(struct pci_dev *dev, int resno);
|
||||
int pci_set_vga_state(struct pci_dev *pdev, bool decode,
|
||||
unsigned int command_bits, u32 flags);
|
||||
|
||||
#define PCI_IRQ_NOLEGACY (1 << 0) /* don't use legacy interrupts */
|
||||
#define PCI_IRQ_NOMSI (1 << 1) /* don't use MSI interrupts */
|
||||
#define PCI_IRQ_NOMSIX (1 << 2) /* don't use MSI-X interrupts */
|
||||
|
||||
/* kmem_cache style wrapper around pci_alloc_consistent() */
|
||||
|
||||
#include <linux/pci-dma.h>
|
||||
@@ -1284,6 +1288,11 @@ static inline int pci_enable_msix_exact(struct pci_dev *dev,
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
int pci_alloc_irq_vectors(struct pci_dev *dev, unsigned int min_vecs,
|
||||
unsigned int max_vecs, unsigned int flags);
|
||||
void pci_free_irq_vectors(struct pci_dev *dev);
|
||||
int pci_irq_vector(struct pci_dev *dev, unsigned int nr);
|
||||
|
||||
#else
|
||||
static inline int pci_msi_vec_count(struct pci_dev *dev) { return -ENOSYS; }
|
||||
static inline void pci_msi_shutdown(struct pci_dev *dev) { }
|
||||
@@ -1307,6 +1316,24 @@ static inline int pci_enable_msix_range(struct pci_dev *dev,
|
||||
static inline int pci_enable_msix_exact(struct pci_dev *dev,
|
||||
struct msix_entry *entries, int nvec)
|
||||
{ return -ENOSYS; }
|
||||
static inline int pci_alloc_irq_vectors(struct pci_dev *dev,
|
||||
unsigned int min_vecs, unsigned int max_vecs,
|
||||
unsigned int flags)
|
||||
{
|
||||
if (min_vecs > 1)
|
||||
return -EINVAL;
|
||||
return 1;
|
||||
}
|
||||
static inline void pci_free_irq_vectors(struct pci_dev *dev)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int pci_irq_vector(struct pci_dev *dev, unsigned int nr)
|
||||
{
|
||||
if (WARN_ON_ONCE(nr > 0))
|
||||
return -EINVAL;
|
||||
return dev->irq;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PCIEPORTBUS
|
||||
|
||||
Reference in New Issue
Block a user