These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / arch / powerpc / kernel / dma.c
index 484b2d4..3f1472a 100644 (file)
@@ -16,6 +16,7 @@
 #include <asm/bug.h>
 #include <asm/machdep.h>
 #include <asm/swiotlb.h>
+#include <asm/iommu.h>
 
 /*
  * Generic direct DMA implementation
@@ -39,9 +40,31 @@ static u64 __maybe_unused get_pfn_limit(struct device *dev)
        return pfn;
 }
 
-void *dma_direct_alloc_coherent(struct device *dev, size_t size,
-                               dma_addr_t *dma_handle, gfp_t flag,
-                               struct dma_attrs *attrs)
+static int dma_direct_dma_supported(struct device *dev, u64 mask)
+{
+#ifdef CONFIG_PPC64
+       u64 limit = get_dma_offset(dev) + (memblock_end_of_DRAM() - 1);
+
+       /* Limit fits in the mask, we are good */
+       if (mask >= limit)
+               return 1;
+
+#ifdef CONFIG_FSL_SOC
+       /* Freescale gets another chance via ZONE_DMA/ZONE_DMA32, however
+        * that will have to be refined if/when they support iommus
+        */
+       return 1;
+#endif
+       /* Sorry ... */
+       return 0;
+#else
+       return 1;
+#endif
+}
+
+void *__dma_direct_alloc_coherent(struct device *dev, size_t size,
+                                 dma_addr_t *dma_handle, gfp_t flag,
+                                 struct dma_attrs *attrs)
 {
        void *ret;
 #ifdef CONFIG_NOT_COHERENT_CACHE
@@ -96,9 +119,9 @@ void *dma_direct_alloc_coherent(struct device *dev, size_t size,
 #endif
 }
 
-void dma_direct_free_coherent(struct device *dev, size_t size,
-                             void *vaddr, dma_addr_t dma_handle,
-                             struct dma_attrs *attrs)
+void __dma_direct_free_coherent(struct device *dev, size_t size,
+                               void *vaddr, dma_addr_t dma_handle,
+                               struct dma_attrs *attrs)
 {
 #ifdef CONFIG_NOT_COHERENT_CACHE
        __dma_free_coherent(size, vaddr);
@@ -107,6 +130,51 @@ void dma_direct_free_coherent(struct device *dev, size_t size,
 #endif
 }
 
+static void *dma_direct_alloc_coherent(struct device *dev, size_t size,
+                                      dma_addr_t *dma_handle, gfp_t flag,
+                                      struct dma_attrs *attrs)
+{
+       struct iommu_table *iommu;
+
+       /* The coherent mask may be smaller than the real mask, check if
+        * we can really use the direct ops
+        */
+       if (dma_direct_dma_supported(dev, dev->coherent_dma_mask))
+               return __dma_direct_alloc_coherent(dev, size, dma_handle,
+                                                  flag, attrs);
+
+       /* Ok we can't ... do we have an iommu ? If not, fail */
+       iommu = get_iommu_table_base(dev);
+       if (!iommu)
+               return NULL;
+
+       /* Try to use the iommu */
+       return iommu_alloc_coherent(dev, iommu, size, dma_handle,
+                                   dev->coherent_dma_mask, flag,
+                                   dev_to_node(dev));
+}
+
+static void dma_direct_free_coherent(struct device *dev, size_t size,
+                                    void *vaddr, dma_addr_t dma_handle,
+                                    struct dma_attrs *attrs)
+{
+       struct iommu_table *iommu;
+
+       /* See comments in dma_direct_alloc_coherent() */
+       if (dma_direct_dma_supported(dev, dev->coherent_dma_mask))
+               return __dma_direct_free_coherent(dev, size, vaddr, dma_handle,
+                                                 attrs);
+       /* Maybe we used an iommu ... */
+       iommu = get_iommu_table_base(dev);
+
+       /* If we hit that we should have never allocated in the first
+        * place so how come we are freeing ?
+        */
+       if (WARN_ON(!iommu))
+               return;
+       iommu_free_coherent(iommu, size, vaddr, dma_handle);
+}
+
 int dma_direct_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
                             void *cpu_addr, dma_addr_t handle, size_t size,
                             struct dma_attrs *attrs)
@@ -147,18 +215,6 @@ static void dma_direct_unmap_sg(struct device *dev, struct scatterlist *sg,
 {
 }
 
-static int dma_direct_dma_supported(struct device *dev, u64 mask)
-{
-#ifdef CONFIG_PPC64
-       /* Could be improved so platforms can set the limit in case
-        * they have limited DMA windows
-        */
-       return mask >= get_dma_offset(dev) + (memblock_end_of_DRAM() - 1);
-#else
-       return 1;
-#endif
-}
-
 static u64 dma_direct_get_required_mask(struct device *dev)
 {
        u64 end, mask;
@@ -230,6 +286,25 @@ struct dma_map_ops dma_direct_ops = {
 };
 EXPORT_SYMBOL(dma_direct_ops);
 
+int dma_set_coherent_mask(struct device *dev, u64 mask)
+{
+       if (!dma_supported(dev, mask)) {
+               /*
+                * We need to special case the direct DMA ops which can
+                * support a fallback for coherent allocations. There
+                * is no dma_op->set_coherent_mask() so we have to do
+                * things the hard way:
+                */
+               if (get_dma_ops(dev) != &dma_direct_ops ||
+                   get_iommu_table_base(dev) == NULL ||
+                   !dma_iommu_dma_supported(dev, mask))
+                       return -EIO;
+       }
+       dev->coherent_dma_mask = mask;
+       return 0;
+}
+EXPORT_SYMBOL(dma_set_coherent_mask);
+
 #define PREALLOC_DMA_DEBUG_ENTRIES (1 << 16)
 
 int __dma_set_mask(struct device *dev, u64 dma_mask)
@@ -248,6 +323,14 @@ int dma_set_mask(struct device *dev, u64 dma_mask)
 {
        if (ppc_md.dma_set_mask)
                return ppc_md.dma_set_mask(dev, dma_mask);
+
+       if (dev_is_pci(dev)) {
+               struct pci_dev *pdev = to_pci_dev(dev);
+               struct pci_controller *phb = pci_bus_to_host(pdev->bus);
+               if (phb->controller_ops.dma_set_mask)
+                       return phb->controller_ops.dma_set_mask(pdev, dma_mask);
+       }
+
        return __dma_set_mask(dev, dma_mask);
 }
 EXPORT_SYMBOL(dma_set_mask);
@@ -270,6 +353,13 @@ u64 dma_get_required_mask(struct device *dev)
        if (ppc_md.dma_get_required_mask)
                return ppc_md.dma_get_required_mask(dev);
 
+       if (dev_is_pci(dev)) {
+               struct pci_dev *pdev = to_pci_dev(dev);
+               struct pci_controller *phb = pci_bus_to_host(pdev->bus);
+               if (phb->controller_ops.dma_get_required_mask)
+                       return phb->controller_ops.dma_get_required_mask(pdev);
+       }
+
        return __dma_get_required_mask(dev);
 }
 EXPORT_SYMBOL_GPL(dma_get_required_mask);