Commit 0fa478df authored by Rob Herring's avatar Rob Herring Committed by Marek Szyprowski

ARM: add coherent iommu dma ops

Remove arch_is_coherent() from iommu dma ops and implement separate
coherent ops functions.
Signed-off-by: default avatarRob Herring <rob.herring@calxeda.com>
Cc: Russell King <linux@arm.linux.org.uk>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: default avatarMarek Szyprowski <m.szyprowski@samsung.com>
parent dd37e940
...@@ -1350,7 +1350,8 @@ static int arm_iommu_get_sgtable(struct device *dev, struct sg_table *sgt, ...@@ -1350,7 +1350,8 @@ static int arm_iommu_get_sgtable(struct device *dev, struct sg_table *sgt,
*/ */
static int __map_sg_chunk(struct device *dev, struct scatterlist *sg, static int __map_sg_chunk(struct device *dev, struct scatterlist *sg,
size_t size, dma_addr_t *handle, size_t size, dma_addr_t *handle,
enum dma_data_direction dir, struct dma_attrs *attrs) enum dma_data_direction dir, struct dma_attrs *attrs,
bool is_coherent)
{ {
struct dma_iommu_mapping *mapping = dev->archdata.mapping; struct dma_iommu_mapping *mapping = dev->archdata.mapping;
dma_addr_t iova, iova_base; dma_addr_t iova, iova_base;
...@@ -1369,8 +1370,8 @@ static int __map_sg_chunk(struct device *dev, struct scatterlist *sg, ...@@ -1369,8 +1370,8 @@ static int __map_sg_chunk(struct device *dev, struct scatterlist *sg,
phys_addr_t phys = page_to_phys(sg_page(s)); phys_addr_t phys = page_to_phys(sg_page(s));
unsigned int len = PAGE_ALIGN(s->offset + s->length); unsigned int len = PAGE_ALIGN(s->offset + s->length);
if (!arch_is_coherent() && if (!is_coherent &&
!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs)) !dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
__dma_page_cpu_to_dev(sg_page(s), s->offset, s->length, dir); __dma_page_cpu_to_dev(sg_page(s), s->offset, s->length, dir);
ret = iommu_map(mapping->domain, iova, phys, len, 0); ret = iommu_map(mapping->domain, iova, phys, len, 0);
...@@ -1388,20 +1389,9 @@ static int __map_sg_chunk(struct device *dev, struct scatterlist *sg, ...@@ -1388,20 +1389,9 @@ static int __map_sg_chunk(struct device *dev, struct scatterlist *sg,
return ret; return ret;
} }
/** static int __iommu_map_sg(struct device *dev, struct scatterlist *sg, int nents,
* arm_iommu_map_sg - map a set of SG buffers for streaming mode DMA enum dma_data_direction dir, struct dma_attrs *attrs,
* @dev: valid struct device pointer bool is_coherent)
* @sg: list of buffers
* @nents: number of buffers to map
* @dir: DMA transfer direction
*
* Map a set of buffers described by scatterlist in streaming mode for DMA.
* The scatter gather list elements are merged together (if possible) and
* tagged with the appropriate dma address and length. They are obtained via
* sg_dma_{address,length}.
*/
int arm_iommu_map_sg(struct device *dev, struct scatterlist *sg, int nents,
enum dma_data_direction dir, struct dma_attrs *attrs)
{ {
struct scatterlist *s = sg, *dma = sg, *start = sg; struct scatterlist *s = sg, *dma = sg, *start = sg;
int i, count = 0; int i, count = 0;
...@@ -1417,7 +1407,7 @@ int arm_iommu_map_sg(struct device *dev, struct scatterlist *sg, int nents, ...@@ -1417,7 +1407,7 @@ int arm_iommu_map_sg(struct device *dev, struct scatterlist *sg, int nents,
if (s->offset || (size & ~PAGE_MASK) || size + s->length > max) { if (s->offset || (size & ~PAGE_MASK) || size + s->length > max) {
if (__map_sg_chunk(dev, start, size, &dma->dma_address, if (__map_sg_chunk(dev, start, size, &dma->dma_address,
dir, attrs) < 0) dir, attrs, is_coherent) < 0)
goto bad_mapping; goto bad_mapping;
dma->dma_address += offset; dma->dma_address += offset;
...@@ -1430,7 +1420,8 @@ int arm_iommu_map_sg(struct device *dev, struct scatterlist *sg, int nents, ...@@ -1430,7 +1420,8 @@ int arm_iommu_map_sg(struct device *dev, struct scatterlist *sg, int nents,
} }
size += s->length; size += s->length;
} }
if (__map_sg_chunk(dev, start, size, &dma->dma_address, dir, attrs) < 0) if (__map_sg_chunk(dev, start, size, &dma->dma_address, dir, attrs,
is_coherent) < 0)
goto bad_mapping; goto bad_mapping;
dma->dma_address += offset; dma->dma_address += offset;
...@@ -1445,17 +1436,44 @@ int arm_iommu_map_sg(struct device *dev, struct scatterlist *sg, int nents, ...@@ -1445,17 +1436,44 @@ int arm_iommu_map_sg(struct device *dev, struct scatterlist *sg, int nents,
} }
/** /**
* arm_iommu_unmap_sg - unmap a set of SG buffers mapped by dma_map_sg * arm_coherent_iommu_map_sg - map a set of SG buffers for streaming mode DMA
* @dev: valid struct device pointer * @dev: valid struct device pointer
* @sg: list of buffers * @sg: list of buffers
* @nents: number of buffers to unmap (same as was passed to dma_map_sg) * @nents: number of buffers to map
* @dir: DMA transfer direction (same as was passed to dma_map_sg) * @dir: DMA transfer direction
* *
* Unmap a set of streaming mode DMA translations. Again, CPU access * Map a set of i/o coherent buffers described by scatterlist in streaming
* rules concerning calls here are the same as for dma_unmap_single(). * mode for DMA. The scatter gather list elements are merged together (if
* possible) and tagged with the appropriate dma address and length. They are
* obtained via sg_dma_{address,length}.
*/ */
void arm_iommu_unmap_sg(struct device *dev, struct scatterlist *sg, int nents, int arm_coherent_iommu_map_sg(struct device *dev, struct scatterlist *sg,
enum dma_data_direction dir, struct dma_attrs *attrs) int nents, enum dma_data_direction dir, struct dma_attrs *attrs)
{
return __iommu_map_sg(dev, sg, nents, dir, attrs, true);
}
/**
* arm_iommu_map_sg - map a set of SG buffers for streaming mode DMA
* @dev: valid struct device pointer
* @sg: list of buffers
* @nents: number of buffers to map
* @dir: DMA transfer direction
*
* Map a set of buffers described by scatterlist in streaming mode for DMA.
* The scatter gather list elements are merged together (if possible) and
* tagged with the appropriate dma address and length. They are obtained via
* sg_dma_{address,length}.
*/
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);
}
static void __iommu_unmap_sg(struct device *dev, struct scatterlist *sg,
int nents, enum dma_data_direction dir, struct dma_attrs *attrs,
bool is_coherent)
{ {
struct scatterlist *s; struct scatterlist *s;
int i; int i;
...@@ -1464,13 +1482,45 @@ void arm_iommu_unmap_sg(struct device *dev, struct scatterlist *sg, int nents, ...@@ -1464,13 +1482,45 @@ void arm_iommu_unmap_sg(struct device *dev, struct scatterlist *sg, int nents,
if (sg_dma_len(s)) if (sg_dma_len(s))
__iommu_remove_mapping(dev, sg_dma_address(s), __iommu_remove_mapping(dev, sg_dma_address(s),
sg_dma_len(s)); sg_dma_len(s));
if (!arch_is_coherent() && if (!is_coherent &&
!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs)) !dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
__dma_page_dev_to_cpu(sg_page(s), s->offset, __dma_page_dev_to_cpu(sg_page(s), s->offset,
s->length, dir); s->length, dir);
} }
} }
/**
* arm_coherent_iommu_unmap_sg - unmap a set of SG buffers mapped by dma_map_sg
* @dev: valid struct device pointer
* @sg: list of buffers
* @nents: number of buffers to unmap (same as was passed to dma_map_sg)
* @dir: DMA transfer direction (same as was passed to dma_map_sg)
*
* Unmap a set of streaming mode DMA translations. Again, CPU access
* rules concerning calls here are the same as for dma_unmap_single().
*/
void arm_coherent_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, true);
}
/**
* arm_iommu_unmap_sg - unmap a set of SG buffers mapped by dma_map_sg
* @dev: valid struct device pointer
* @sg: list of buffers
* @nents: number of buffers to unmap (same as was passed to dma_map_sg)
* @dir: DMA transfer direction (same as was passed to dma_map_sg)
*
* Unmap a set of streaming mode DMA translations. Again, CPU access
* rules concerning calls here are the same as for dma_unmap_single().
*/
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);
}
/** /**
* arm_iommu_sync_sg_for_cpu * arm_iommu_sync_sg_for_cpu
* @dev: valid struct device pointer * @dev: valid struct device pointer
...@@ -1485,8 +1535,7 @@ void arm_iommu_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, ...@@ -1485,8 +1535,7 @@ void arm_iommu_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg,
int i; int i;
for_each_sg(sg, s, nents, i) for_each_sg(sg, s, nents, i)
if (!arch_is_coherent()) __dma_page_dev_to_cpu(sg_page(s), s->offset, s->length, dir);
__dma_page_dev_to_cpu(sg_page(s), s->offset, s->length, dir);
} }
...@@ -1504,22 +1553,21 @@ void arm_iommu_sync_sg_for_device(struct device *dev, struct scatterlist *sg, ...@@ -1504,22 +1553,21 @@ void arm_iommu_sync_sg_for_device(struct device *dev, struct scatterlist *sg,
int i; int i;
for_each_sg(sg, s, nents, i) for_each_sg(sg, s, nents, i)
if (!arch_is_coherent()) __dma_page_cpu_to_dev(sg_page(s), s->offset, s->length, dir);
__dma_page_cpu_to_dev(sg_page(s), s->offset, s->length, dir);
} }
/** /**
* arm_iommu_map_page * arm_coherent_iommu_map_page
* @dev: valid struct device pointer * @dev: valid struct device pointer
* @page: page that buffer resides in * @page: page that buffer resides in
* @offset: offset into page for start of buffer * @offset: offset into page for start of buffer
* @size: size of buffer to map * @size: size of buffer to map
* @dir: DMA transfer direction * @dir: DMA transfer direction
* *
* IOMMU aware version of arm_dma_map_page() * Coherent IOMMU aware version of arm_dma_map_page()
*/ */
static dma_addr_t arm_iommu_map_page(struct device *dev, struct page *page, static dma_addr_t arm_coherent_iommu_map_page(struct device *dev, struct page *page,
unsigned long offset, size_t size, enum dma_data_direction dir, unsigned long offset, size_t size, enum dma_data_direction dir,
struct dma_attrs *attrs) struct dma_attrs *attrs)
{ {
...@@ -1527,9 +1575,6 @@ static dma_addr_t arm_iommu_map_page(struct device *dev, struct page *page, ...@@ -1527,9 +1575,6 @@ static dma_addr_t arm_iommu_map_page(struct device *dev, struct page *page,
dma_addr_t dma_addr; dma_addr_t dma_addr;
int ret, len = PAGE_ALIGN(size + offset); int ret, len = PAGE_ALIGN(size + offset);
if (!arch_is_coherent() && !dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
__dma_page_cpu_to_dev(page, offset, size, dir);
dma_addr = __alloc_iova(mapping, len); dma_addr = __alloc_iova(mapping, len);
if (dma_addr == DMA_ERROR_CODE) if (dma_addr == DMA_ERROR_CODE)
return dma_addr; return dma_addr;
...@@ -1544,6 +1589,52 @@ static dma_addr_t arm_iommu_map_page(struct device *dev, struct page *page, ...@@ -1544,6 +1589,52 @@ static dma_addr_t arm_iommu_map_page(struct device *dev, struct page *page,
return DMA_ERROR_CODE; return DMA_ERROR_CODE;
} }
/**
* arm_iommu_map_page
* @dev: valid struct device pointer
* @page: page that buffer resides in
* @offset: offset into page for start of buffer
* @size: size of buffer to map
* @dir: DMA transfer direction
*
* IOMMU aware version of arm_dma_map_page()
*/
static dma_addr_t arm_iommu_map_page(struct device *dev, struct page *page,
unsigned long offset, size_t size, enum dma_data_direction dir,
struct dma_attrs *attrs)
{
if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
__dma_page_cpu_to_dev(page, offset, size, dir);
return arm_coherent_iommu_map_page(dev, page, offset, size, dir, attrs);
}
/**
* arm_coherent_iommu_unmap_page
* @dev: valid struct device pointer
* @handle: DMA address of buffer
* @size: size of buffer (same as passed to dma_map_page)
* @dir: DMA transfer direction (same as passed to dma_map_page)
*
* Coherent IOMMU aware version of arm_dma_unmap_page()
*/
static void arm_coherent_iommu_unmap_page(struct device *dev, dma_addr_t handle,
size_t size, enum dma_data_direction dir,
struct dma_attrs *attrs)
{
struct dma_iommu_mapping *mapping = dev->archdata.mapping;
dma_addr_t iova = handle & PAGE_MASK;
struct page *page = phys_to_page(iommu_iova_to_phys(mapping->domain, iova));
int offset = handle & ~PAGE_MASK;
int len = PAGE_ALIGN(size + offset);
if (!iova)
return;
iommu_unmap(mapping->domain, iova, len);
__free_iova(mapping, iova, len);
}
/** /**
* arm_iommu_unmap_page * arm_iommu_unmap_page
* @dev: valid struct device pointer * @dev: valid struct device pointer
...@@ -1566,7 +1657,7 @@ static void arm_iommu_unmap_page(struct device *dev, dma_addr_t handle, ...@@ -1566,7 +1657,7 @@ static void arm_iommu_unmap_page(struct device *dev, dma_addr_t handle,
if (!iova) if (!iova)
return; return;
if (!arch_is_coherent() && !dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs)) if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
__dma_page_dev_to_cpu(page, offset, size, dir); __dma_page_dev_to_cpu(page, offset, size, dir);
iommu_unmap(mapping->domain, iova, len); iommu_unmap(mapping->domain, iova, len);
...@@ -1584,8 +1675,7 @@ static void arm_iommu_sync_single_for_cpu(struct device *dev, ...@@ -1584,8 +1675,7 @@ static void arm_iommu_sync_single_for_cpu(struct device *dev,
if (!iova) if (!iova)
return; return;
if (!arch_is_coherent()) __dma_page_dev_to_cpu(page, offset, size, dir);
__dma_page_dev_to_cpu(page, offset, size, dir);
} }
static void arm_iommu_sync_single_for_device(struct device *dev, static void arm_iommu_sync_single_for_device(struct device *dev,
...@@ -1619,6 +1709,19 @@ struct dma_map_ops iommu_ops = { ...@@ -1619,6 +1709,19 @@ struct dma_map_ops iommu_ops = {
.sync_sg_for_device = arm_iommu_sync_sg_for_device, .sync_sg_for_device = arm_iommu_sync_sg_for_device,
}; };
struct dma_map_ops iommu_coherent_ops = {
.alloc = arm_iommu_alloc_attrs,
.free = arm_iommu_free_attrs,
.mmap = arm_iommu_mmap_attrs,
.get_sgtable = arm_iommu_get_sgtable,
.map_page = arm_coherent_iommu_map_page,
.unmap_page = arm_coherent_iommu_unmap_page,
.map_sg = arm_coherent_iommu_map_sg,
.unmap_sg = arm_coherent_iommu_unmap_sg,
};
/** /**
* arm_iommu_create_mapping * arm_iommu_create_mapping
* @bus: pointer to the bus holding the client device (for IOMMU calls) * @bus: pointer to the bus holding the client device (for IOMMU calls)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment