BACKPORT: FROMGIT: PCI: dwc: Add support for 64-bit MSI target address

Since not all devices require a 32-bit MSI address, add support to the
PCIe host driver to allow setting the DMA mask to 64-bits if the 32-bit
allocation fails. This allows kernels to disable ZONE_DMA32 and bounce
buffering (swiotlb) without risking not being able to get a 32-bit address
during DMA allocation.

Basically, in the slim chance that there are no 32-bit allocations
available, the current PCIe host driver will fail to allocate the msi_msg
page due to a DMA address overflow (seen in [1]). With this patch, the
PCIe host can retry the allocation with a 64-bit DMA mask if the current
PCIe device advertises 64-bit support via its MSI capabilities.

[1] https://lore.kernel.org/all/Yo0soniFborDl7+C@google.com/

Bug: 241473543
Link: https://lore.kernel.org/r/20220825235404.4132818-3-willmcvicker@google.com
Reported-by: kernel test robot <lkp@intel.com>
Signed-off-by: Will McVicker <willmcvicker@google.com>
Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Reviewed-by: Rob Herring <robh@kernel.org>
Acked-by: Jingoo Han <jingoohan1@gmail.com>
Change-Id: I8936717b26f3dec453b0a944b26a0bb891905100
(cherry picked from commit e99d8c5e803b9a9f0b5a84165dad3b8895446147 https://git.kernel.org/pub/scm/linux/kernel/git/lpieralisi/pci.git pci/dwc)
This commit is contained in:
Will McVicker
2022-08-25 23:54:03 +00:00
committed by Treehugger Robot
parent 8181ea8d96
commit 8de5430cb7
3 changed files with 25 additions and 3 deletions

View File

@@ -373,9 +373,22 @@ int dw_pcie_host_init(struct pcie_port *pp)
msi_vaddr = dmam_alloc_coherent(dev, sizeof(u64), &pp->msi_data,
GFP_KERNEL);
if (!msi_vaddr) {
dev_err(dev, "Failed to alloc and map MSI data\n");
ret = -ENOMEM;
goto err_free_msi;
u16 msi_capabilities;
/* Retry the allocation with a 64-bit mask if supported. */
msi_capabilities = dw_pcie_msi_capabilities(pci);
if ((msi_capabilities & PCI_MSI_FLAGS_ENABLE) &&
(msi_capabilities & PCI_MSI_FLAGS_64BIT)) {
dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
msi_vaddr = dmam_alloc_coherent(dev, sizeof(u64),
&pp->msi_data,
GFP_KERNEL);
}
if (!msi_vaddr) {
dev_err(dev, "Failed to alloc and map MSI data\n");
ret = -ENOMEM;
goto err_free_msi;
}
}
}
}

View File

@@ -55,6 +55,14 @@ u8 dw_pcie_find_capability(struct dw_pcie *pci, u8 cap)
}
EXPORT_SYMBOL_GPL(dw_pcie_find_capability);
u16 dw_pcie_msi_capabilities(struct dw_pcie *pci)
{
u8 offset;
offset = dw_pcie_find_capability(pci, PCI_CAP_ID_MSI);
return dw_pcie_readw_dbi(pci, offset + PCI_MSI_FLAGS);
}
static u16 dw_pcie_find_next_ext_capability(struct dw_pcie *pci, u16 start,
u8 cap)
{

View File

@@ -285,6 +285,7 @@ struct dw_pcie {
u8 dw_pcie_find_capability(struct dw_pcie *pci, u8 cap);
u16 dw_pcie_find_ext_capability(struct dw_pcie *pci, u8 cap);
u16 dw_pcie_msi_capabilities(struct dw_pcie *pci);
int dw_pcie_read(void __iomem *addr, int size, u32 *val);
int dw_pcie_write(void __iomem *addr, int size, u32 val);