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

Commit 6018182d authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

Merge branch 'pci/msi' into next

* 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
parents 65a129d7 a579ba49
Loading
Loading
Loading
Loading
+75 −96
Original line number Diff line number Diff line
@@ -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;
		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);
		}
	}

		/*
		 * 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."
	return nvec;
}

/**
 * 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()
 *
		 * "For MSI-X, the [DPC Interrupt Message Number] indicates
		 * which MSI-X Table entry is used to generate the
		 * interrupt message."
 * Return value: 0 on success, error code on failure
 */
		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;
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;

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

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

/**
+2 −0
Original line number Diff line number Diff line
@@ -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 */
@@ -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 */