Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3321eafd authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

PCI/portdrv: Factor out Interrupt Message Number lookup



Factor out Interrupt Message Number lookup from the MSI/MSI-X interrupt
setup.  One side effect is that we only have to check once to see if we
have enough vectors for all the services.  No functional change intended.

Signed-off-by: default avatarBjorn Helgaas <bhelgaas@google.com>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent b8acfd7c
Loading
Loading
Loading
Loading
+62 −48
Original line number Diff line number Diff line
@@ -43,24 +43,16 @@ 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;

	/* 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;
	u32 nvec = 0, pos, reg32;
	u16 reg16;

	/*
	 * The Interrupt Message Number indicates which vector is used, i.e.,
@@ -68,49 +60,75 @@ static int pcie_port_enable_irq_vec(struct pci_dev *dev, int *irqs, int mask)
	 * Data and the generated interrupt message.  See PCIe r3.1, sec
	 * 7.8.2, 7.10.10, 7.31.2.
	 */
	if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP)) {
		u16 reg16;

	if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP)) {
		pcie_capability_read_word(dev, PCI_EXP_FLAGS, &reg16);
		entry = (reg16 & PCI_EXP_FLAGS_IRQ) >> 9;
		if (entry >= nr_entries)
			goto out_free_irqs;

		/* PME and hotplug share an MSI/MSI-X vector */
		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;

		pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
		pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &reg32);
		entry = (reg32 & PCI_ERR_ROOT_AER_IRQ) >> 27;
		if (entry >= nr_entries)
			goto out_free_irqs;
		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);
		}
	}

		irqs[PCIE_PORT_SERVICE_AER_SHIFT] = pci_irq_vector(dev, entry);
	if (mask & PCIE_PORT_SERVICE_DPC) {
		pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC);
		if (pos) {
			pci_read_config_word(dev, pos + PCI_EXP_DPC_CAP,
					     &reg16);
			*dpc = reg16 & PCI_EXP_DPC_IRQ;
			nvec = max(nvec, *dpc + 1);
		}
	}

		nvec = max(nvec, entry + 1);
	return nvec;
}

	if (mask & PCIE_PORT_SERVICE_DPC) {
		u16 reg16, pos;
/**
 * 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;

		pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC);
		pci_read_config_word(dev, pos + PCI_EXP_DPC_CAP, &reg16);
		entry = reg16 & PCI_EXP_DPC_IRQ;
		if (entry >= nr_entries)
			goto out_free_irqs;
	/* 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;

		irqs[PCIE_PORT_SERVICE_DPC_SHIFT] = pci_irq_vector(dev, entry);
	/* 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;
	}

		nvec = max(nvec, entry + 1);
	/* 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);
	}

	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);

	/* If we allocated more than we need, free them and allocate fewer */
	if (nvec != nr_entries) {
		pci_free_irq_vectors(dev);
@@ -122,10 +140,6 @@ static int pcie_port_enable_irq_vec(struct pci_dev *dev, int *irqs, int mask)
	}

	return 0;

out_free_irqs:
	pci_free_irq_vectors(dev);
	return -EIO;
}

/**