Skip to content

Commit

Permalink
Merge branch 'pci/msi' into next
Browse files Browse the repository at this point in the history
* pci/msi:
  PCI/portdrv: Compute MSI/MSI-X IRQ vectors after final allocation
  PCI/portdrv: Factor out Interrupt Message Number lookup
  PCI/portdrv: Consolidate comments
  PCI/portdrv: Add #defines for AER and DPC Interrupt Message Number masks
  • Loading branch information
bjorn-helgaas committed Nov 14, 2017
2 parents 65a129d + a579ba4 commit 6018182
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 96 deletions.
171 changes: 75 additions & 96 deletions drivers/pci/pcie/portdrv_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,134 +43,113 @@ static void release_pcie_device(struct device *dev)
kfree(to_pcie_device(dev));
}

/**
* pcie_port_enable_irq_vec - try to set up MSI-X or MSI as interrupt mode
* for given port
* @dev: PCI Express port to handle
* @irqs: Array of interrupt vectors to populate
* @mask: Bitmask of port capabilities returned by get_port_device_capability()
*
* Return value: 0 on success, error code on failure
/*
* Fill in *pme, *aer, *dpc with the relevant Interrupt Message Numbers if
* services are enabled in "mask". Return the number of MSI/MSI-X vectors
* required to accommodate the largest Message Number.
*/
static int pcie_port_enable_irq_vec(struct pci_dev *dev, int *irqs, int mask)
static int pcie_message_numbers(struct pci_dev *dev, int mask,
u32 *pme, u32 *aer, u32 *dpc)
{
int nr_entries, entry, nvec = 0;
u32 nvec = 0, pos, reg32;
u16 reg16;

/*
* Allocate as many entries as the port wants, so that we can check
* which of them will be useful. Moreover, if nr_entries is correctly
* equal to the number of entries this port actually uses, we'll happily
* go through without any tricks.
* The Interrupt Message Number indicates which vector is used, i.e.,
* the MSI-X table entry or the MSI offset between the base Message
* Data and the generated interrupt message. See PCIe r3.1, sec
* 7.8.2, 7.10.10, 7.31.2.
*/
nr_entries = pci_alloc_irq_vectors(dev, 1, PCIE_PORT_MAX_MSI_ENTRIES,
PCI_IRQ_MSIX | PCI_IRQ_MSI);
if (nr_entries < 0)
return nr_entries;

if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP)) {
u16 reg16;

/*
* Per PCIe r3.1, sec 6.1.6, "PME and Hot-Plug Event
* interrupts (when both are implemented) always share the
* same MSI or MSI-X vector, as indicated by the Interrupt
* Message Number field in the PCI Express Capabilities
* register".
*
* Per sec 7.8.2, "For MSI, the [Interrupt Message Number]
* indicates the offset between the base Message Data and
* the interrupt message that is generated."
*
* "For MSI-X, the [Interrupt Message Number] indicates
* which MSI-X Table entry is used to generate the
* interrupt message."
*/
pcie_capability_read_word(dev, PCI_EXP_FLAGS, &reg16);
entry = (reg16 & PCI_EXP_FLAGS_IRQ) >> 9;
if (entry >= nr_entries)
goto out_free_irqs;

irqs[PCIE_PORT_SERVICE_PME_SHIFT] = pci_irq_vector(dev, entry);
irqs[PCIE_PORT_SERVICE_HP_SHIFT] = pci_irq_vector(dev, entry);

nvec = max(nvec, entry + 1);
*pme = (reg16 & PCI_EXP_FLAGS_IRQ) >> 9;
nvec = *pme + 1;
}

if (mask & PCIE_PORT_SERVICE_AER) {
u32 reg32, pos;

/*
* Per PCIe r3.1, sec 7.10.10, the Advanced Error Interrupt
* Message Number in the Root Error Status register
* indicates which MSI/MSI-X vector is used for AER.
*
* "For MSI, the [Advanced Error Interrupt Message Number]
* indicates the offset between the base Message Data and
* the interrupt message that is generated."
*
* "For MSI-X, the [Advanced Error Interrupt Message
* Number] indicates which MSI-X Table entry is used to
* generate the interrupt message."
*/
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &reg32);
entry = reg32 >> 27;
if (entry >= nr_entries)
goto out_free_irqs;

irqs[PCIE_PORT_SERVICE_AER_SHIFT] = pci_irq_vector(dev, entry);

nvec = max(nvec, entry + 1);
if (pos) {
pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS,
&reg32);
*aer = (reg32 & PCI_ERR_ROOT_AER_IRQ) >> 27;
nvec = max(nvec, *aer + 1);
}
}

if (mask & PCIE_PORT_SERVICE_DPC) {
u16 reg16, pos;

/*
* Per PCIe r4.0 (v0.9), sec 7.9.15.2, the DPC Interrupt
* Message Number in the DPC Capability register indicates
* which MSI/MSI-X vector is used for DPC.
*
* "For MSI, the [DPC Interrupt Message Number] indicates
* the offset between the base Message Data and the
* interrupt message that is generated."
*
* "For MSI-X, the [DPC Interrupt Message Number] indicates
* which MSI-X Table entry is used to generate the
* interrupt message."
*/
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC);
pci_read_config_word(dev, pos + PCI_EXP_DPC_CAP, &reg16);
entry = reg16 & 0x1f;
if (entry >= nr_entries)
goto out_free_irqs;
if (pos) {
pci_read_config_word(dev, pos + PCI_EXP_DPC_CAP,
&reg16);
*dpc = reg16 & PCI_EXP_DPC_IRQ;
nvec = max(nvec, *dpc + 1);
}
}

return nvec;
}

irqs[PCIE_PORT_SERVICE_DPC_SHIFT] = pci_irq_vector(dev, entry);
/**
* pcie_port_enable_irq_vec - try to set up MSI-X or MSI as interrupt mode
* for given port
* @dev: PCI Express port to handle
* @irqs: Array of interrupt vectors to populate
* @mask: Bitmask of port capabilities returned by get_port_device_capability()
*
* Return value: 0 on success, error code on failure
*/
static int pcie_port_enable_irq_vec(struct pci_dev *dev, int *irqs, int mask)
{
int nr_entries, nvec;
u32 pme = 0, aer = 0, dpc = 0;

nvec = max(nvec, entry + 1);
/* Allocate the maximum possible number of MSI/MSI-X vectors */
nr_entries = pci_alloc_irq_vectors(dev, 1, PCIE_PORT_MAX_MSI_ENTRIES,
PCI_IRQ_MSIX | PCI_IRQ_MSI);
if (nr_entries < 0)
return nr_entries;

/* See how many and which Interrupt Message Numbers we actually use */
nvec = pcie_message_numbers(dev, mask, &pme, &aer, &dpc);
if (nvec > nr_entries) {
pci_free_irq_vectors(dev);
return -EIO;
}

/*
* If nvec is equal to the allocated number of entries, we can just use
* what we have. Otherwise, the port has some extra entries not for the
* services we know and we need to work around that.
* If we allocated more than we need, free them and reallocate fewer.
*
* Reallocating may change the specific vectors we get, so
* pci_irq_vector() must be done *after* the reallocation.
*
* If we're using MSI, hardware is *allowed* to change the Interrupt
* Message Numbers when we free and reallocate the vectors, but we
* assume it won't because we allocate enough vectors for the
* biggest Message Number we found.
*/
if (nvec != nr_entries) {
/* Drop the temporary MSI-X setup */
pci_free_irq_vectors(dev);

/* Now allocate the MSI-X vectors for real */
nr_entries = pci_alloc_irq_vectors(dev, nvec, nvec,
PCI_IRQ_MSIX | PCI_IRQ_MSI);
if (nr_entries < 0)
return nr_entries;
}

return 0;
/* PME and hotplug share an MSI/MSI-X vector */
if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP)) {
irqs[PCIE_PORT_SERVICE_PME_SHIFT] = pci_irq_vector(dev, pme);
irqs[PCIE_PORT_SERVICE_HP_SHIFT] = pci_irq_vector(dev, pme);
}

out_free_irqs:
pci_free_irq_vectors(dev);
return -EIO;
if (mask & PCIE_PORT_SERVICE_AER)
irqs[PCIE_PORT_SERVICE_AER_SHIFT] = pci_irq_vector(dev, aer);

if (mask & PCIE_PORT_SERVICE_DPC)
irqs[PCIE_PORT_SERVICE_DPC_SHIFT] = pci_irq_vector(dev, dpc);

return 0;
}

/**
Expand Down
2 changes: 2 additions & 0 deletions include/uapi/linux/pci_regs.h
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,7 @@
#define PCI_ERR_ROOT_FIRST_FATAL 0x00000010 /* First UNC is Fatal */
#define PCI_ERR_ROOT_NONFATAL_RCV 0x00000020 /* Non-Fatal Received */
#define PCI_ERR_ROOT_FATAL_RCV 0x00000040 /* Fatal Received */
#define PCI_ERR_ROOT_AER_IRQ 0xf8000000 /* Advanced Error Interrupt Message Number */
#define PCI_ERR_ROOT_ERR_SRC 52 /* Error Source Identification */

/* Virtual Channel */
Expand Down Expand Up @@ -960,6 +961,7 @@

/* Downstream Port Containment */
#define PCI_EXP_DPC_CAP 4 /* DPC Capability */
#define PCI_EXP_DPC_IRQ 0x1f /* DPC Interrupt Message Number */
#define PCI_EXP_DPC_CAP_RP_EXT 0x20 /* Root Port Extensions for DPC */
#define PCI_EXP_DPC_CAP_POISONED_TLP 0x40 /* Poisoned TLP Egress Blocking Supported */
#define PCI_EXP_DPC_CAP_SW_TRIGGER 0x80 /* Software Triggering Supported */
Expand Down

0 comments on commit 6018182

Please sign in to comment.