|
@@ -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_GPL(dma_set_coherent_mask);
|
|
|
+
|
|
|
#define PREALLOC_DMA_DEBUG_ENTRIES (1 << 16)
|
|
|
|
|
|
int __dma_set_mask(struct device *dev, u64 dma_mask)
|