it is possible that PCI device supports 64-bit DMA addressing, and thus it's driver sets device's dma_mask to DMA_BIT_MASK(64), however PCI host bridge may have limitations on the inbound transaction addressing. As an example, consider NVME SSD device connected to iproc-PCIe controller. Currently, the IOMMU DMA ops only considers PCI device dma_mask when allocating an IOVA. This is particularly problematic on ARM/ARM64 SOCs where the IOMMU (i.e. SMMU) translates IOVA to PA for in-bound transactions only after PCI Host has forwarded these transactions on SOC IO bus. This means on such ARM/ARM64 SOCs the IOVA of in-bound transactions has to honor the addressing restrictions of the PCI Host. this patch calls pci specific of_pci_dma_get_ranges, instead of calling memory-mapped one, which returns wrong size and also not meant for PCI world. with this, it gets accurate resources back, and largest possible inbound window size. with that largest possible dma_mask can be generated. Signed-off-by: Oza Pawandeep <oza.oza@xxxxxxxxxxxx> diff --git a/drivers/of/device.c b/drivers/of/device.c index b1e6beb..d6a8dde 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c @@ -9,6 +9,7 @@ #include <linux/module.h> #include <linux/mod_devicetable.h> #include <linux/slab.h> +#include <linux/of_pci.h> #include <asm/errno.h> #include "of_private.h" @@ -89,6 +90,8 @@ void of_dma_configure(struct device *dev, struct device_node *np) bool coherent; unsigned long offset; const struct iommu_ops *iommu; + struct resource_entry *window; + LIST_HEAD(res); /* * Set default coherent_dma_mask to 32 bit. Drivers are expected to @@ -104,7 +107,24 @@ void of_dma_configure(struct device *dev, struct device_node *np) if (!dev->dma_mask) dev->dma_mask = &dev->coherent_dma_mask; - ret = of_dma_get_range(np, &dma_addr, &paddr, &size); + if (dev_is_pci(dev)) { + size = 0; + ret = of_pci_get_dma_ranges(np, &res); + if (!ret) { + resource_list_for_each_entry(window, &res) { + struct resource *res_dma = window->res; + if (size < resource_size(res_dma)) { + dma_addr = res_dma->start - window->offset; + paddr = res_dma->start; + size = resource_size(res_dma); + } + } + } + pci_free_resource_list(&res); + } + else + ret = of_dma_get_range(np, &dma_addr, &paddr, &size); + if (ret < 0) { dma_addr = offset = 0; size = dev->coherent_dma_mask + 1; -- 1.9.1