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
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,
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);
}
/**