]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
arm64: dma-mapping: map sg lists into the SMMU as virtually contiguous
authorMitchel Humpherys <mitchelh@codeaurora.org>
Tue, 28 Oct 2014 20:45:02 +0000 (13:45 -0700)
committerSrinivas Kandagatla <srinivas.kandagatla@linaro.org>
Mon, 11 Jan 2016 09:54:40 +0000 (09:54 +0000)
In arm_iommu_map_sg, currently we map each individual link in the given
scatterlist into the SMMU individually such that they may or may not be
virtually contiguous.  However, in most (all?) of our use cases we
actually want the entire sg list mapped into the SMMU as a single
contiguous range.  Use iommu_map_range to accomplish this.

Signed-off-by: Mitchel Humpherys <mitchelh@codeaurora.org>
[Forward Ported this from msm3.14]
Signed-off-by: Sricharan R <sricharan@codeaurora.org>
[added iommu_map_range in generic dma code]
Signed-off-by: Stanimir Varbanov <stanimir.varbanov@linaro.org>
arch/arm64/mm/dma-mapping.c

index 3467425b259c5f05a10458ed1da7ae4a73330aff..2de4fbb7d8a9cf678fa89830124d0ad0c6655026 100644 (file)
@@ -1559,6 +1559,39 @@ int arm_coherent_iommu_map_sg(struct device *dev, struct scatterlist *sg,
        return __iommu_map_sg(dev, sg, nents, dir, attrs, true);
 }
 
+static int iommu_map_range(struct iommu_domain *domain, unsigned int iova,
+                   struct scatterlist *sg, unsigned int len, int opt)
+{
+       s32 ret = 0;
+       u32 offset = 0;
+       u32 start_iova = iova;
+
+       BUG_ON(iova & (~PAGE_MASK));
+
+       while (offset < len) {
+               phys_addr_t phys = page_to_phys(sg_page(sg));
+               u32 page_len = PAGE_ALIGN(sg->offset + sg->length);
+
+               ret = iommu_map(domain, iova, phys, page_len, opt);
+               if (ret)
+                       goto fail;
+
+               iova += page_len;
+               offset += page_len;
+               if (offset < len)
+                       sg = sg_next(sg);
+       }
+
+       goto out;
+
+fail:
+       /* undo mappings already done in case of error */
+       iommu_unmap(domain, start_iova, offset);
+out:
+
+       return ret;
+}
+
 /**
  * arm_iommu_map_sg - map a set of SG buffers for streaming mode DMA
  * @dev: valid struct device pointer
@@ -1574,7 +1607,29 @@ int arm_coherent_iommu_map_sg(struct device *dev, struct scatterlist *sg,
 int arm_iommu_map_sg(struct device *dev, struct scatterlist *sg,
                int nents, enum dma_data_direction dir, struct dma_attrs *attrs)
 {
-       return __iommu_map_sg(dev, sg, nents, dir, attrs, false);
+       struct scatterlist *s;
+       int ret, i;
+       struct dma_iommu_mapping *mapping = dev->archdata.mapping;
+       unsigned int iova, total_length = 0, current_offset = 0;
+       int prot = __dma_direction_to_prot(dir);
+
+       for_each_sg(sg, s, nents, i)
+               total_length += s->length;
+
+       iova = __alloc_iova(mapping, total_length);
+       ret = iommu_map_range(mapping->domain, iova, sg, total_length, prot);
+       if (ret) {
+               __free_iova(mapping, iova, total_length);
+               return 0;
+       }
+
+       for_each_sg(sg, s, nents, i) {
+               s->dma_address = iova + current_offset;
+               s->dma_length = total_length - current_offset;
+               current_offset += s->length;
+       }
+
+       return nents;
 }
 
 static void __iommu_unmap_sg(struct device *dev, struct scatterlist *sg,
@@ -1624,7 +1679,15 @@ void arm_coherent_iommu_unmap_sg(struct device *dev, struct scatterlist *sg,
 void arm_iommu_unmap_sg(struct device *dev, struct scatterlist *sg, int nents,
                        enum dma_data_direction dir, struct dma_attrs *attrs)
 {
-       __iommu_unmap_sg(dev, sg, nents, dir, attrs, false);
+       struct dma_iommu_mapping *mapping = dev->archdata.mapping;
+       unsigned int total_length = sg_dma_len(sg);
+       unsigned int iova = sg_dma_address(sg);
+
+       total_length = PAGE_ALIGN((iova & ~PAGE_MASK) + total_length);
+       iova &= PAGE_MASK;
+
+       iommu_unmap(mapping->domain, iova, total_length);
+       __free_iova(mapping, iova, total_length);
 }
 
 /**