diff options
Diffstat (limited to 'drivers/pci/msi/msi.c')
-rw-r--r-- | drivers/pci/msi/msi.c | 167 |
1 files changed, 60 insertions, 107 deletions
diff --git a/drivers/pci/msi/msi.c b/drivers/pci/msi/msi.c index 7058d59e7c5f..6569ba3577fe 100644 --- a/drivers/pci/msi/msi.c +++ b/drivers/pci/msi/msi.c @@ -335,11 +335,41 @@ static int msi_verify_entries(struct pci_dev *dev) return !entry ? 0 : -EIO; } -static int __msi_capability_init(struct pci_dev *dev, int nvec, struct irq_affinity_desc *masks) +/** + * msi_capability_init - configure device's MSI capability structure + * @dev: pointer to the pci_dev data structure of MSI device function + * @nvec: number of interrupts to allocate + * @affd: description of automatic IRQ affinity assignments (may be %NULL) + * + * Setup the MSI capability structure of the device with the requested + * number of interrupts. A return value of zero indicates the successful + * setup of an entry with the new MSI IRQ. A negative return value indicates + * an error, and a positive return value indicates the number of interrupts + * which could have been allocated. + */ +static int msi_capability_init(struct pci_dev *dev, int nvec, + struct irq_affinity *affd) { - int ret = msi_setup_msi_desc(dev, nvec, masks); + struct irq_affinity_desc *masks = NULL; struct msi_desc *entry, desc; + int ret; + /* Reject multi-MSI early on irq domain enabled architectures */ + if (nvec > 1 && !pci_msi_domain_supports(dev, MSI_FLAG_MULTI_PCI_MSI, ALLOW_LEGACY)) + return 1; + + /* + * Disable MSI during setup in the hardware, but mark it enabled + * so that setup code can evaluate it. + */ + pci_msi_set_enable(dev, 0); + dev->msi_enabled = 1; + + if (affd) + masks = irq_create_affinity_masks(nvec, affd); + + msi_lock_descs(&dev->dev); + ret = msi_setup_msi_desc(dev, nvec, masks); if (ret) goto fail; @@ -368,48 +398,19 @@ static int __msi_capability_init(struct pci_dev *dev, int nvec, struct irq_affin pcibios_free_irq(dev); dev->irq = entry->irq; - return 0; + goto unlock; + err: pci_msi_unmask(&desc, msi_multi_mask(&desc)); pci_free_msi_irqs(dev); fail: dev->msi_enabled = 0; +unlock: + msi_unlock_descs(&dev->dev); + kfree(masks); return ret; } -/** - * msi_capability_init - configure device's MSI capability structure - * @dev: pointer to the pci_dev data structure of MSI device function - * @nvec: number of interrupts to allocate - * @affd: description of automatic IRQ affinity assignments (may be %NULL) - * - * Setup the MSI capability structure of the device with the requested - * number of interrupts. A return value of zero indicates the successful - * setup of an entry with the new MSI IRQ. A negative return value indicates - * an error, and a positive return value indicates the number of interrupts - * which could have been allocated. - */ -static int msi_capability_init(struct pci_dev *dev, int nvec, - struct irq_affinity *affd) -{ - /* Reject multi-MSI early on irq domain enabled architectures */ - if (nvec > 1 && !pci_msi_domain_supports(dev, MSI_FLAG_MULTI_PCI_MSI, ALLOW_LEGACY)) - return 1; - - /* - * Disable MSI during setup in the hardware, but mark it enabled - * so that setup code can evaluate it. - */ - pci_msi_set_enable(dev, 0); - dev->msi_enabled = 1; - - struct irq_affinity_desc *masks __free(kfree) = - affd ? irq_create_affinity_masks(nvec, affd) : NULL; - - guard(msi_descs_lock)(&dev->dev); - return __msi_capability_init(dev, nvec, masks); -} - int __pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec, struct irq_affinity *affd) { @@ -662,41 +663,40 @@ static void msix_mask_all(void __iomem *base, int tsize) writel(ctrl, base + PCI_MSIX_ENTRY_VECTOR_CTRL); } -static int __msix_setup_interrupts(struct pci_dev *dev, struct msix_entry *entries, - int nvec, struct irq_affinity_desc *masks) +static int msix_setup_interrupts(struct pci_dev *dev, struct msix_entry *entries, + int nvec, struct irq_affinity *affd) { - int ret = msix_setup_msi_descs(dev, entries, nvec, masks); + struct irq_affinity_desc *masks = NULL; + int ret; + + if (affd) + masks = irq_create_affinity_masks(nvec, affd); + msi_lock_descs(&dev->dev); + ret = msix_setup_msi_descs(dev, entries, nvec, masks); if (ret) - goto fail; + goto out_free; ret = pci_msi_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSIX); if (ret) - goto fail; + goto out_free; /* Check if all MSI entries honor device restrictions */ ret = msi_verify_entries(dev); if (ret) - goto fail; + goto out_free; msix_update_entries(dev, entries); - return 0; + goto out_unlock; -fail: +out_free: pci_free_msi_irqs(dev); +out_unlock: + msi_unlock_descs(&dev->dev); + kfree(masks); return ret; } -static int msix_setup_interrupts(struct pci_dev *dev, struct msix_entry *entries, - int nvec, struct irq_affinity *affd) -{ - struct irq_affinity_desc *masks __free(kfree) = - affd ? irq_create_affinity_masks(nvec, affd) : NULL; - - guard(msi_descs_lock)(&dev->dev); - return __msix_setup_interrupts(dev, entries, nvec, masks); -} - /** * msix_capability_init - configure device's MSI-X capability * @dev: pointer to the pci_dev data structure of MSI-X device function @@ -870,13 +870,13 @@ void __pci_restore_msix_state(struct pci_dev *dev) write_msg = arch_restore_msi_irqs(dev); - scoped_guard (msi_descs_lock, &dev->dev) { - msi_for_each_desc(entry, &dev->dev, MSI_DESC_ALL) { - if (write_msg) - __pci_write_msi_msg(entry, &entry->msg); - pci_msix_write_vector_ctrl(entry, entry->pci.msix_ctrl); - } + msi_lock_descs(&dev->dev); + msi_for_each_desc(entry, &dev->dev, MSI_DESC_ALL) { + if (write_msg) + __pci_write_msi_msg(entry, &entry->msg); + pci_msix_write_vector_ctrl(entry, entry->pci.msix_ctrl); } + msi_unlock_descs(&dev->dev); pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_MASKALL, 0); } @@ -915,53 +915,6 @@ void pci_free_msi_irqs(struct pci_dev *dev) } } -#ifdef CONFIG_PCIE_TPH -/** - * pci_msix_write_tph_tag - Update the TPH tag for a given MSI-X vector - * @pdev: The PCIe device to update - * @index: The MSI-X index to update - * @tag: The tag to write - * - * Returns: 0 on success, error code on failure - */ -int pci_msix_write_tph_tag(struct pci_dev *pdev, unsigned int index, u16 tag) -{ - struct msi_desc *msi_desc; - struct irq_desc *irq_desc; - unsigned int virq; - - if (!pdev->msix_enabled) - return -ENXIO; - - guard(msi_descs_lock)(&pdev->dev); - virq = msi_get_virq(&pdev->dev, index); - if (!virq) - return -ENXIO; - /* - * This is a horrible hack, but short of implementing a PCI - * specific interrupt chip callback and a huge pile of - * infrastructure, this is the minor nuissance. It provides the - * protection against concurrent operations on this entry and keeps - * the control word cache in sync. - */ - irq_desc = irq_to_desc(virq); - if (!irq_desc) - return -ENXIO; - - guard(raw_spinlock_irq)(&irq_desc->lock); - msi_desc = irq_data_get_msi_desc(&irq_desc->irq_data); - if (!msi_desc || msi_desc->pci.msi_attrib.is_virtual) - return -ENXIO; - - msi_desc->pci.msix_ctrl &= ~PCI_MSIX_ENTRY_CTRL_ST; - msi_desc->pci.msix_ctrl |= FIELD_PREP(PCI_MSIX_ENTRY_CTRL_ST, tag); - pci_msix_write_vector_ctrl(msi_desc, msi_desc->pci.msix_ctrl); - /* Flush the write */ - readl(pci_msix_desc_addr(msi_desc)); - return 0; -} -#endif - /* Misc. infrastructure */ struct pci_dev *msi_desc_to_pci_dev(struct msi_desc *desc) |