[RFC PATCH v2 1/3] iommu: implement common IOMMU ops for DMA mapping
Robin Murphy
robin.murphy at arm.com
Fri Feb 6 06:55:13 PST 2015
Taking inspiration from the existing arch/arm code, break out some
generic functions to interface the DMA-API to the IOMMU-API. This will
do the bulk of the heavy lifting for IOMMU-backed dma-mapping.
Whilst this RFC series is aimed at enabling arm64, once any remaining
obvious issues in the common code are addressed we can complete the
refactoring by porting arch/arm over for a merge-worthy series.
Signed-off-by: Robin Murphy <robin.murphy at arm.com>
---
drivers/iommu/Kconfig | 7 +
drivers/iommu/Makefile | 1 +
drivers/iommu/dma-iommu.c | 552 ++++++++++++++++++++++++++++++++++++++++++++++
include/linux/dma-iommu.h | 94 ++++++++
4 files changed, 654 insertions(+)
create mode 100644 drivers/iommu/dma-iommu.c
create mode 100644 include/linux/dma-iommu.h
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index a839ca9..19027bb 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -20,6 +20,13 @@ config OF_IOMMU
def_bool y
depends on OF && IOMMU_API
+# IOMMU-agnostic DMA-mapping layer
+config IOMMU_DMA
+ def_bool n
+ depends on NEED_SG_DMA_LENGTH
+ select IOMMU_API
+ select IOMMU_IOVA
+
config FSL_PAMU
bool "Freescale IOMMU support"
depends on PPC_E500MC
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index 0b1b94e..37bfc4e 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -1,6 +1,7 @@
obj-$(CONFIG_IOMMU_API) += iommu.o
obj-$(CONFIG_IOMMU_API) += iommu-traces.o
obj-$(CONFIG_IOMMU_API) += iommu-sysfs.o
+obj-$(CONFIG_IOMMU_DMA) += dma-iommu.o
obj-$(CONFIG_IOMMU_IOVA) += iova.o
obj-$(CONFIG_OF_IOMMU) += of_iommu.o
obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
new file mode 100644
index 0000000..b97cc0b9
--- /dev/null
+++ b/drivers/iommu/dma-iommu.c
@@ -0,0 +1,552 @@
+/*
+ * A fairly generic DMA-API to IOMMU-API glue layer.
+ *
+ * Copyright (C) 2014 ARM Ltd.
+ *
+ * based in part on arch/arm/mm/dma-mapping.c:
+ * Copyright (C) 2000-2004 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/dma-contiguous.h>
+#include <linux/dma-iommu.h>
+#include <linux/iova.h>
+
+int iommu_dma_init(void)
+{
+ return iommu_iova_cache_init();
+}
+
+struct iommu_dma_domain {
+ struct iommu_domain *domain;
+ struct iova_domain *iovad;
+ struct kref kref;
+};
+
+static inline dma_addr_t dev_dma_addr(struct device *dev, dma_addr_t addr)
+{
+ BUG_ON(addr < dev->dma_pfn_offset);
+ return addr - ((dma_addr_t)dev->dma_pfn_offset << PAGE_SHIFT);
+}
+
+static int __dma_direction_to_prot(enum dma_data_direction dir, bool coherent)
+{
+ int prot = coherent ? IOMMU_CACHE : 0;
+
+ switch (dir) {
+ case DMA_BIDIRECTIONAL:
+ return prot | IOMMU_READ | IOMMU_WRITE;
+ case DMA_TO_DEVICE:
+ return prot | IOMMU_READ;
+ case DMA_FROM_DEVICE:
+ return prot | IOMMU_WRITE;
+ default:
+ return 0;
+ }
+}
+
+static struct iova *__alloc_iova(struct device *dev, size_t size, bool coherent)
+{
+ struct iommu_dma_domain *dom = get_dma_domain(dev);
+ struct iova_domain *iovad = dom->iovad;
+ unsigned long shift = iova_shift(iovad);
+ unsigned long length = iova_align(iovad, size) >> shift;
+ unsigned long limit_pfn = iovad->dma_32bit_pfn;
+ u64 dma_limit = coherent ? dev->coherent_dma_mask : *dev->dma_mask;
+
+ limit_pfn = min(limit_pfn, (unsigned long)(dma_limit >> shift));
+ /* Alignment should probably come from a domain/device attribute... */
+ return alloc_iova(iovad, length, limit_pfn, false);
+}
+
+/*
+ * Create a mapping in device IO address space for specified pages
+ */
+dma_addr_t iommu_dma_create_iova_mapping(struct device *dev,
+ struct page **pages, size_t size, bool coherent)
+{
+ struct iommu_dma_domain *dom = get_dma_domain(dev);
+ struct iova_domain *iovad = dom->iovad;
+ struct iommu_domain *domain = dom->domain;
+ struct iova *iova;
+ unsigned int count = PAGE_ALIGN(size) >> PAGE_SHIFT;
+ dma_addr_t addr_lo, addr_hi;
+ int i, prot = __dma_direction_to_prot(DMA_BIDIRECTIONAL, coherent);
+
+ iova = __alloc_iova(dev, size, coherent);
+ if (!iova)
+ return DMA_ERROR_CODE;
+
+ addr_hi = addr_lo = iova_dma_addr(iovad, iova);
+ for (i = 0; i < count; ) {
+ unsigned int next_pfn = page_to_pfn(pages[i]) + 1;
+ phys_addr_t phys = page_to_phys(pages[i]);
+ unsigned int len, j;
+
+ for (j = i+1; j < count; j++, next_pfn++)
+ if (page_to_pfn(pages[j]) != next_pfn)
+ break;
+
+ len = (j - i) << PAGE_SHIFT;
+ if (iommu_map(domain, addr_hi, phys, len, prot))
+ goto fail;
+ addr_hi += len;
+ i = j;
+ }
+ return dev_dma_addr(dev, addr_lo);
+fail:
+ iommu_unmap(domain, addr_lo, addr_hi - addr_lo);
+ __free_iova(iovad, iova);
+ return DMA_ERROR_CODE;
+}
+
+int iommu_dma_release_iova_mapping(struct device *dev, dma_addr_t iova,
+ size_t size)
+{
+ struct iommu_dma_domain *dom = get_dma_domain(dev);
+ struct iova_domain *iovad = dom->iovad;
+ size_t offset = iova_offset(iovad, iova);
+ size_t len = iova_align(iovad, size + offset);
+
+ iommu_unmap(dom->domain, iova - offset, len);
+ free_iova(iovad, iova_pfn(iovad, iova));
+ return 0;
+}
+
+struct page **iommu_dma_alloc_buffer(struct device *dev, size_t size,
+ gfp_t gfp, struct dma_attrs *attrs,
+ void (clear_buffer)(struct page *page, size_t size))
+{
+ struct page **pages;
+ int count = size >> PAGE_SHIFT;
+ int array_size = count * sizeof(struct page *);
+ int i = 0;
+
+ if (array_size <= PAGE_SIZE)
+ pages = kzalloc(array_size, GFP_KERNEL);
+ else
+ pages = vzalloc(array_size);
+ if (!pages)
+ return NULL;
+
+ if (dma_get_attr(DMA_ATTR_FORCE_CONTIGUOUS, attrs)) {
+ unsigned long order = get_order(size);
+ struct page *page;
+
+ page = dma_alloc_from_contiguous(dev, count, order);
+ if (!page)
+ goto error;
+
+ if (clear_buffer)
+ clear_buffer(page, size);
+
+ for (i = 0; i < count; i++)
+ pages[i] = page + i;
+
+ return pages;
+ }
+
+ /*
+ * IOMMU can map any pages, so himem can also be used here
+ */
+ gfp |= __GFP_NOWARN | __GFP_HIGHMEM;
+
+ while (count) {
+ int j, order = __fls(count);
+
+ pages[i] = alloc_pages(gfp, order);
+ while (!pages[i] && order)
+ pages[i] = alloc_pages(gfp, --order);
+ if (!pages[i])
+ goto error;
+
+ if (order) {
+ split_page(pages[i], order);
+ j = 1 << order;
+ while (--j)
+ pages[i + j] = pages[i] + j;
+ }
+
+ if (clear_buffer)
+ clear_buffer(pages[i], PAGE_SIZE << order);
+ i += 1 << order;
+ count -= 1 << order;
+ }
+
+ return pages;
+error:
+ while (i--)
+ if (pages[i])
+ __free_pages(pages[i], 0);
+ if (array_size <= PAGE_SIZE)
+ kfree(pages);
+ else
+ vfree(pages);
+ return NULL;
+}
+
+int iommu_dma_free_buffer(struct device *dev, struct page **pages, size_t size,
+ struct dma_attrs *attrs)
+{
+ int count = size >> PAGE_SHIFT;
+ int array_size = count * sizeof(struct page *);
+ int i;
+
+ if (dma_get_attr(DMA_ATTR_FORCE_CONTIGUOUS, attrs)) {
+ dma_release_from_contiguous(dev, pages[0], count);
+ } else {
+ for (i = 0; i < count; i++)
+ if (pages[i])
+ __free_pages(pages[i], 0);
+ }
+
+ if (array_size <= PAGE_SIZE)
+ kfree(pages);
+ else
+ vfree(pages);
+ return 0;
+}
+
+static dma_addr_t __iommu_dma_map_page(struct device *dev, struct page *page,
+ unsigned long offset, size_t size, enum dma_data_direction dir,
+ bool coherent)
+{
+ dma_addr_t dma_addr;
+ struct iommu_dma_domain *dom = get_dma_domain(dev);
+ struct iova_domain *iovad = dom->iovad;
+ phys_addr_t phys = page_to_phys(page) + offset;
+ size_t iova_off = iova_offset(iovad, phys);
+ size_t len = iova_align(iovad, size + iova_off);
+ int prot = __dma_direction_to_prot(dir, coherent);
+ struct iova *iova = __alloc_iova(dev, len, coherent);
+
+ if (!iova)
+ return DMA_ERROR_CODE;
+
+ dma_addr = iova_dma_addr(iovad, iova);
+ if (iommu_map(dom->domain, dma_addr, phys - iova_off, len, prot)) {
+ __free_iova(iovad, iova);
+ return DMA_ERROR_CODE;
+ }
+
+ return dev_dma_addr(dev, dma_addr + iova_off);
+}
+
+dma_addr_t iommu_dma_map_page(struct device *dev, struct page *page,
+ unsigned long offset, size_t size, enum dma_data_direction dir,
+ struct dma_attrs *attrs)
+{
+ return __iommu_dma_map_page(dev, page, offset, size, dir, false);
+}
+
+dma_addr_t iommu_dma_coherent_map_page(struct device *dev, struct page *page,
+ unsigned long offset, size_t size, enum dma_data_direction dir,
+ struct dma_attrs *attrs)
+{
+ return __iommu_dma_map_page(dev, page, offset, size, dir, true);
+}
+
+void iommu_dma_unmap_page(struct device *dev, dma_addr_t handle, size_t size,
+ enum dma_data_direction dir, struct dma_attrs *attrs)
+{
+ struct iommu_dma_domain *dom = get_dma_domain(dev);
+ struct iova_domain *iovad = dom->iovad;
+ size_t offset = iova_offset(iovad, handle);
+ size_t len = iova_align(iovad, size + offset);
+ dma_addr_t iova = handle - offset;
+
+ if (!iova)
+ return;
+
+ iommu_unmap(dom->domain, iova, len);
+ free_iova(iovad, iova_pfn(iovad, iova));
+}
+
+static int finalise_sg(struct device *dev, struct scatterlist *sg, int nents,
+ dma_addr_t dma_addr)
+{
+ struct scatterlist *s, *seg = sg;
+ unsigned long seg_mask = dma_get_seg_boundary(dev);
+ unsigned int max_len = dma_get_max_seg_size(dev);
+ unsigned int seg_len = 0, seg_dma = 0;
+ int i, count = 1;
+
+ for_each_sg(sg, s, nents, i) {
+ /* Un-swizzling the fields here, hence the naming mismatch */
+ unsigned int s_offset = sg_dma_address(s);
+ unsigned int s_length = sg_dma_len(s);
+ unsigned int s_dma_len = s->length;
+
+ s->offset = s_offset;
+ s->length = s_length;
+ sg_dma_address(s) = DMA_ERROR_CODE;
+ sg_dma_len(s) = 0;
+
+ if (seg_len && (seg_dma + seg_len == dma_addr + s_offset) &&
+ (seg_len + s_dma_len <= max_len) &&
+ ((seg_dma & seg_mask) <= seg_mask - (seg_len + s_length))
+ ) {
+ sg_dma_len(seg) += s_dma_len;
+ } else {
+ if (seg_len) {
+ seg = sg_next(seg);
+ count++;
+ }
+ sg_dma_len(seg) = s_dma_len;
+ sg_dma_address(seg) = dma_addr + s_offset;
+
+ seg_len = s_offset;
+ seg_dma = dma_addr + s_offset;
+ }
+ seg_len += s_length;
+ dma_addr += s_dma_len;
+ }
+ return count;
+}
+
+static void invalidate_sg(struct scatterlist *sg, int nents)
+{
+ struct scatterlist *s;
+ int i;
+
+ for_each_sg(sg, s, nents, i) {
+ if (sg_dma_address(s) != DMA_ERROR_CODE)
+ s->offset = sg_dma_address(s);
+ if (sg_dma_len(s))
+ s->length = sg_dma_len(s);
+ sg_dma_address(s) = DMA_ERROR_CODE;
+ sg_dma_len(s) = 0;
+ }
+}
+
+static int __iommu_dma_map_sg(struct device *dev, struct scatterlist *sg,
+ int nents, enum dma_data_direction dir, struct dma_attrs *attrs,
+ bool coherent)
+{
+ struct iommu_dma_domain *dom = get_dma_domain(dev);
+ struct iova_domain *iovad = dom->iovad;
+ struct iova *iova;
+ struct scatterlist *s;
+ dma_addr_t dma_addr;
+ size_t iova_len = 0;
+ int i, prot = __dma_direction_to_prot(dir, coherent);
+
+ /*
+ * Work out how much IOVA space we need, and align the segments to
+ * IOVA granules for the IOMMU driver to handle. With some clever
+ * trickery we can modify the list in a reversible manner.
+ */
+ for_each_sg(sg, s, nents, i) {
+ size_t s_offset = iova_offset(iovad, s->offset);
+ size_t s_length = s->length;
+
+ sg_dma_address(s) = s->offset;
+ sg_dma_len(s) = s_length;
+ s->offset -= s_offset;
+ s_length = iova_align(iovad, s_length + s_offset);
+ s->length = s_length;
+
+ iova_len += s_length;
+ }
+
+ iova = __alloc_iova(dev, iova_len, coherent);
+ if (!iova)
+ goto out_restore_sg;
+
+ /*
+ * We'll leave any physical concatenation to the IOMMU driver's
+ * implementation - it knows better than we do.
+ */
+ dma_addr = iova_dma_addr(iovad, iova);
+ if (iommu_map_sg(dom->domain, dma_addr, sg, nents, prot) < iova_len)
+ goto out_free_iova;
+
+ return finalise_sg(dev, sg, nents, dev_dma_addr(dev, dma_addr));
+
+out_free_iova:
+ __free_iova(iovad, iova);
+out_restore_sg:
+ invalidate_sg(sg, nents);
+ return 0;
+}
+
+int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
+ enum dma_data_direction dir, struct dma_attrs *attrs)
+{
+ return __iommu_dma_map_sg(dev, sg, nents, dir, attrs, false);
+}
+
+int iommu_dma_coherent_map_sg(struct device *dev, struct scatterlist *sg,
+ int nents, enum dma_data_direction dir, struct dma_attrs *attrs)
+{
+ return __iommu_dma_map_sg(dev, sg, nents, dir, attrs, true);
+}
+
+void iommu_dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nents,
+ enum dma_data_direction dir, struct dma_attrs *attrs)
+{
+ struct iommu_dma_domain *dom = get_dma_domain(dev);
+ struct iova_domain *iovad = dom->iovad;
+ struct scatterlist *s;
+ int i;
+ dma_addr_t iova = sg_dma_address(sg) & ~iova_mask(iovad);
+ size_t len = 0;
+
+ /*
+ * The scatterlist segments are mapped into contiguous IOVA space,
+ * so just add up the total length and unmap it in one go.
+ */
+ for_each_sg(sg, s, nents, i)
+ len += sg_dma_len(s);
+
+ iommu_unmap(dom->domain, iova, len);
+ free_iova(iovad, iova_pfn(iovad, iova));
+}
+
+struct iommu_dma_domain *iommu_dma_create_domain(const struct iommu_ops *ops,
+ dma_addr_t base, size_t size)
+{
+ struct iommu_dma_domain *dom;
+ struct iommu_domain *domain;
+ struct iova_domain *iovad;
+ struct iommu_domain_geometry *dg;
+ unsigned long order, base_pfn, end_pfn;
+
+ pr_debug("base=%pad\tsize=0x%zx\n", &base, size);
+ dom = kzalloc(sizeof(*dom), GFP_KERNEL);
+ if (!dom)
+ return NULL;
+
+ /*
+ * HACK: We'd like to ask the relevant IOMMU in ops for a suitable
+ * domain, but until that happens, bypass the bus nonsense and create
+ * one directly for this specific device/IOMMU combination...
+ */
+ domain = kzalloc(sizeof(*domain), GFP_KERNEL);
+
+ if (!domain)
+ goto out_free_dma_domain;
+ domain->ops = ops;
+
+ if (ops->domain_init(domain))
+ goto out_free_iommu_domain;
+ /*
+ * ...and do the bare minimum to sanity-check that the domain allows
+ * at least some access to the device...
+ */
+ dg = &domain->geometry;
+ if (!(base <= dg->aperture_end && base + size > dg->aperture_start)) {
+ pr_warn("DMA range outside IOMMU capability; is DT correct?\n");
+ goto out_free_iommu_domain;
+ }
+ /* ...then finally give it a kicking to make sure it fits */
+ dg->aperture_start = max(base, dg->aperture_start);
+ dg->aperture_end = min(base + size - 1, dg->aperture_end);
+ /*
+ * Note that this almost certainly breaks the case where multiple
+ * devices with different DMA capabilities need to share a domain,
+ * but we don't have the necessary information to handle that here
+ * anyway - "proper" group and domain allocation needs to involve
+ * the IOMMU driver and a complete view of the bus.
+ */
+
+ iovad = kzalloc(sizeof(*iovad), GFP_KERNEL);
+ if (!iovad)
+ goto out_free_iommu_domain;
+
+ /* Use the smallest supported page size for IOVA granularity */
+ order = __ffs(ops->pgsize_bitmap);
+ base_pfn = max(dg->aperture_start >> order, (dma_addr_t)1);
+ end_pfn = dg->aperture_end >> order;
+ init_iova_domain(iovad, 1UL << order, base_pfn, end_pfn);
+
+ dom->domain = domain;
+ dom->iovad = iovad;
+ kref_init(&dom->kref);
+ pr_debug("domain %p created\n", dom);
+ return dom;
+
+out_free_iommu_domain:
+ kfree(domain);
+out_free_dma_domain:
+ kfree(dom);
+ return NULL;
+}
+
+static void iommu_dma_free_domain(struct kref *kref)
+{
+ struct iommu_dma_domain *dom;
+
+ dom = container_of(kref, struct iommu_dma_domain, kref);
+ put_iova_domain(dom->iovad);
+ iommu_domain_free(dom->domain);
+ kfree(dom);
+ pr_debug("domain %p freed\n", dom);
+}
+
+void iommu_dma_release_domain(struct iommu_dma_domain *dom)
+{
+ kref_put(&dom->kref, iommu_dma_free_domain);
+}
+
+struct iommu_domain *iommu_dma_raw_domain(struct iommu_dma_domain *dom)
+{
+ return dom ? dom->domain : NULL;
+}
+
+int iommu_dma_attach_device(struct device *dev, struct iommu_dma_domain *dom)
+{
+ int ret;
+
+ kref_get(&dom->kref);
+ ret = iommu_attach_device(dom->domain, dev);
+ if (ret)
+ iommu_dma_release_domain(dom);
+ else
+ set_dma_domain(dev, dom);
+ pr_debug("%s%s attached to domain %p\n", dev_name(dev),
+ ret?" *not*":"", dom);
+ return ret;
+}
+
+void iommu_dma_detach_device(struct device *dev)
+{
+ struct iommu_dma_domain *dom = get_dma_domain(dev);
+
+ if (!dom) {
+ dev_warn(dev, "Not attached\n");
+ return;
+ }
+ set_dma_domain(dev, NULL);
+ iommu_detach_device(dom->domain, dev);
+ iommu_dma_release_domain(dom);
+ pr_debug("%s detached from domain %p\n", dev_name(dev), dom);
+}
+
+int iommu_dma_supported(struct device *dev, u64 mask)
+{
+ /*
+ * This looks awful, but really it's reasonable to assume that if an
+ * IOMMU can't address everything that the CPU can, it probably isn't
+ * generic enough to be using this implementation in the first place.
+ */
+ return 1;
+}
+
+int iommu_dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
+{
+ return dma_addr == DMA_ERROR_CODE;
+}
diff --git a/include/linux/dma-iommu.h b/include/linux/dma-iommu.h
new file mode 100644
index 0000000..4bba85a
--- /dev/null
+++ b/include/linux/dma-iommu.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2014 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __DMA_IOMMU_H
+#define __DMA_IOMMU_H
+
+#ifdef __KERNEL__
+
+#include <linux/types.h>
+#include <linux/iommu.h>
+
+#ifdef CONFIG_IOMMU_DMA
+
+int iommu_dma_init(void);
+
+
+struct iommu_dma_domain *iommu_dma_create_domain(const struct iommu_ops *ops,
+ dma_addr_t base, size_t size);
+void iommu_dma_release_domain(struct iommu_dma_domain *dma_domain);
+
+struct iommu_domain *iommu_dma_raw_domain(struct iommu_dma_domain *dma_domain);
+
+int iommu_dma_attach_device(struct device *dev, struct iommu_dma_domain *domain);
+void iommu_dma_detach_device(struct device *dev);
+
+/*
+ * Implementation of these is left to arch code - it can associate domains
+ * with devices however it likes, provided the lookup is efficient.
+ */
+struct iommu_dma_domain *get_dma_domain(struct device *dev);
+void set_dma_domain(struct device *dev, struct iommu_dma_domain *dma_domain);
+
+
+dma_addr_t iommu_dma_create_iova_mapping(struct device *dev,
+ struct page **pages, size_t size, bool coherent);
+int iommu_dma_release_iova_mapping(struct device *dev, dma_addr_t iova,
+ size_t size);
+
+struct page **iommu_dma_alloc_buffer(struct device *dev, size_t size,
+ gfp_t gfp, struct dma_attrs *attrs,
+ void (clear_buffer)(struct page *page, size_t size));
+int iommu_dma_free_buffer(struct device *dev, struct page **pages, size_t size,
+ struct dma_attrs *attrs);
+
+dma_addr_t iommu_dma_map_page(struct device *dev, struct page *page,
+ unsigned long offset, size_t size, enum dma_data_direction dir,
+ struct dma_attrs *attrs);
+dma_addr_t iommu_dma_coherent_map_page(struct device *dev, struct page *page,
+ unsigned long offset, size_t size, enum dma_data_direction dir,
+ struct dma_attrs *attrs);
+void iommu_dma_unmap_page(struct device *dev, dma_addr_t handle, size_t size,
+ enum dma_data_direction dir, struct dma_attrs *attrs);
+
+int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
+ enum dma_data_direction dir, struct dma_attrs *attrs);
+int iommu_dma_coherent_map_sg(struct device *dev, struct scatterlist *sg,
+ int nents, enum dma_data_direction dir,
+ struct dma_attrs *attrs);
+void iommu_dma_unmap_sg(struct device *dev, struct scatterlist *sgl, int nents,
+ enum dma_data_direction dir, struct dma_attrs *attrs);
+
+int iommu_dma_supported(struct device *dev, u64 mask);
+int iommu_dma_mapping_error(struct device *dev, dma_addr_t dma_addr);
+
+#else
+
+static inline int iommu_dma_init(void)
+{
+ return 0;
+}
+
+static inline struct iommu_dma_domain *get_dma_domain(struct device *dev)
+{
+ return NULL;
+}
+
+void set_dma_domain(struct device *dev, struct iommu_dma_domain *dma_domain) { }
+
+#endif /* CONFIG_IOMMU_DMA */
+
+#endif /* __KERNEL__ */
+#endif /* __DMA_IOMMU_H */
--
1.9.1
More information about the linux-arm-kernel
mailing list