PCI: tegra: Use the DMA-API to get the MSI address

Since the upstream MSI memory writes are generated by downstream
devices, it is logically correct to have MSI target memory coming from
the DMA pool reserved for PCIe than from the general memory pool
reserved for CPU access to avoid PCIe DMA addresses coinciding with
MSI target address thereby raising unwanted MSI interrupts.

Enforce this behaviour by retrieving the MSI address through the DMA
API.

Limit the MSI target address to 32-bits to make it work for PCIe
endpoints that support only 32-bit MSI target address; endpoints that
support 64-bit MSI target address work with 32-bit MSI target
address too.

Signed-off-by: Vidya Sagar <vidyas@nvidia.com>
[lorenzo.pieralisi@arm.com: updated commit log]
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Reviewed-by: Robin Murphy <robin.murphy@arm.com>
Reviewed-by: Thierry Reding <treding@nvidia.com>
Acked-by: Thierry Reding <treding@nvidia.com>
This commit is contained in:
Vidya Sagar 2019-04-16 16:21:44 +05:30 committed by Lorenzo Pieralisi
parent 9e98c678c2
commit 21e2079fe4

View File

@ -231,9 +231,9 @@ struct tegra_msi {
struct msi_controller chip;
DECLARE_BITMAP(used, INT_PCI_MSI_NR);
struct irq_domain *domain;
unsigned long pages;
struct mutex lock;
u64 phys;
void *virt;
dma_addr_t phys;
int irq;
};
@ -1536,7 +1536,7 @@ static int tegra_pcie_msi_setup(struct tegra_pcie *pcie)
err = platform_get_irq_byname(pdev, "msi");
if (err < 0) {
dev_err(dev, "failed to get IRQ: %d\n", err);
goto err;
goto free_irq_domain;
}
msi->irq = err;
@ -1545,17 +1545,35 @@ static int tegra_pcie_msi_setup(struct tegra_pcie *pcie)
tegra_msi_irq_chip.name, pcie);
if (err < 0) {
dev_err(dev, "failed to request IRQ: %d\n", err);
goto err;
goto free_irq_domain;
}
/* Though the PCIe controller can address >32-bit address space, to
* facilitate endpoints that support only 32-bit MSI target address,
* the mask is set to 32-bit to make sure that MSI target address is
* always a 32-bit address
*/
err = dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
if (err < 0) {
dev_err(dev, "failed to set DMA coherent mask: %d\n", err);
goto free_irq;
}
msi->virt = dma_alloc_attrs(dev, PAGE_SIZE, &msi->phys, GFP_KERNEL,
DMA_ATTR_NO_KERNEL_MAPPING);
if (!msi->virt) {
dev_err(dev, "failed to allocate DMA memory for MSI\n");
err = -ENOMEM;
goto free_irq;
}
/* setup AFI/FPCI range */
msi->pages = __get_free_pages(GFP_KERNEL, 0);
msi->phys = virt_to_phys((void *)msi->pages);
host->msi = &msi->chip;
return 0;
err:
free_irq:
free_irq(msi->irq, pcie);
free_irq_domain:
irq_domain_remove(msi->domain);
return err;
}
@ -1592,7 +1610,8 @@ static void tegra_pcie_msi_teardown(struct tegra_pcie *pcie)
struct tegra_msi *msi = &pcie->msi;
unsigned int i, irq;
free_pages(msi->pages, 0);
dma_free_attrs(pcie->dev, PAGE_SIZE, msi->virt, msi->phys,
DMA_ATTR_NO_KERNEL_MAPPING);
if (msi->irq > 0)
free_irq(msi->irq, pcie);