diff options
Diffstat (limited to 'meta/recipes-kernel/linux/linux-omap-2.6.29/isp/iommu/0004-omap-iommu-simple-virtual-address-space-management.patch')
-rw-r--r-- | meta/recipes-kernel/linux/linux-omap-2.6.29/isp/iommu/0004-omap-iommu-simple-virtual-address-space-management.patch | 1083 |
1 files changed, 0 insertions, 1083 deletions
diff --git a/meta/recipes-kernel/linux/linux-omap-2.6.29/isp/iommu/0004-omap-iommu-simple-virtual-address-space-management.patch b/meta/recipes-kernel/linux/linux-omap-2.6.29/isp/iommu/0004-omap-iommu-simple-virtual-address-space-management.patch deleted file mode 100644 index 945778b94..000000000 --- a/meta/recipes-kernel/linux/linux-omap-2.6.29/isp/iommu/0004-omap-iommu-simple-virtual-address-space-management.patch +++ /dev/null @@ -1,1083 +0,0 @@ -From 07365182b998af3dc2b79e822b8e21a3f50262c4 Mon Sep 17 00:00:00 2001 -From: Hiroshi DOYU <Hiroshi.DOYU@nokia.com> -Date: Wed, 28 Jan 2009 21:32:08 +0200 -Subject: [PATCH] omap iommu: simple virtual address space management - -This patch provides a device drivers, which has a omap iommu, with -address mapping APIs between device virtual address(iommu), physical -address and MPU virtual address. - -There are 4 possible patterns for iommu virtual address(iova/da) mapping. - - |iova/ mapping iommu_ page - | da pa va (d)-(p)-(v) function type - --------------------------------------------------------------------------- - 1 | c c c 1 - 1 - 1 _kmap() / _kunmap() s - 2 | c c,a c 1 - 1 - 1 _kmalloc()/ _kfree() s - 3 | c d c 1 - n - 1 _vmap() / _vunmap() s - 4 | c d,a c 1 - n - 1 _vmalloc()/ _vfree() n* - - 'iova': device iommu virtual address - 'da': alias of 'iova' - 'pa': physical address - 'va': mpu virtual address - - 'c': contiguous memory area - 'd': dicontiguous memory area - 'a': anonymous memory allocation - '()': optional feature - - 'n': a normal page(4KB) size is used. - 's': multiple iommu superpage(16MB, 1MB, 64KB, 4KB) size is used. - - '*': not yet, but feasible. - -Signed-off-by: Hiroshi DOYU <Hiroshi.DOYU@nokia.com> ---- - arch/arm/include/asm/io.h | 6 + - arch/arm/mm/ioremap.c | 11 + - arch/arm/plat-omap/include/mach/iovmm.h | 94 ++++ - arch/arm/plat-omap/iovmm.c | 891 +++++++++++++++++++++++++++++++ - 4 files changed, 1002 insertions(+), 0 deletions(-) - create mode 100644 arch/arm/plat-omap/include/mach/iovmm.h - create mode 100644 arch/arm/plat-omap/iovmm.c - -diff --git a/arch/arm/include/asm/io.h b/arch/arm/include/asm/io.h -index d2a59cf..cbdadfe 100644 ---- a/arch/arm/include/asm/io.h -+++ b/arch/arm/include/asm/io.h -@@ -75,6 +75,12 @@ extern void __iomem * __arm_ioremap(unsigned long, size_t, unsigned int); - extern void __iounmap(volatile void __iomem *addr); - - /* -+ * external interface to remap single page with appropriate type -+ */ -+extern int ioremap_page(unsigned long virt, unsigned long phys, -+ unsigned int mtype); -+ -+/* - * Bad read/write accesses... - */ - extern void __readwrite_bug(const char *fn); -diff --git a/arch/arm/mm/ioremap.c b/arch/arm/mm/ioremap.c -index 9f88dd3..8441351 100644 ---- a/arch/arm/mm/ioremap.c -+++ b/arch/arm/mm/ioremap.c -@@ -110,6 +110,17 @@ static int remap_area_pages(unsigned long start, unsigned long pfn, - return err; - } - -+int ioremap_page(unsigned long virt, unsigned long phys, unsigned int mtype) -+{ -+ const struct mem_type *type; -+ -+ type = get_mem_type(mtype); -+ if (!type) -+ return -EINVAL; -+ -+ return remap_area_pages(virt, __phys_to_pfn(phys), PAGE_SIZE, type); -+} -+EXPORT_SYMBOL(ioremap_page); - - void __check_kvm_seq(struct mm_struct *mm) - { -diff --git a/arch/arm/plat-omap/include/mach/iovmm.h b/arch/arm/plat-omap/include/mach/iovmm.h -new file mode 100644 -index 0000000..bdc7ce5 ---- /dev/null -+++ b/arch/arm/plat-omap/include/mach/iovmm.h -@@ -0,0 +1,94 @@ -+/* -+ * omap iommu: simple virtual address space management -+ * -+ * Copyright (C) 2008-2009 Nokia Corporation -+ * -+ * Written by Hiroshi DOYU <Hiroshi.DOYU@nokia.com> -+ * -+ * 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. -+ */ -+ -+#ifndef __IOMMU_MMAP_H -+#define __IOMMU_MMAP_H -+ -+struct iovm_struct { -+ struct iommu *iommu; /* iommu object which this belongs to */ -+ u32 da_start; /* area definition */ -+ u32 da_end; -+ u32 flags; /* IOVMF_: see below */ -+ struct list_head list; /* linked in ascending order */ -+ const struct sg_table *sgt; /* keep 'page' <-> 'da' mapping */ -+ void *va; /* mpu side mapped address */ -+}; -+ -+/* -+ * IOVMF_FLAGS: attribute for iommu virtual memory area(iovma) -+ * -+ * lower 16 bit is used for h/w and upper 16 bit is for s/w. -+ */ -+#define IOVMF_SW_SHIFT 16 -+#define IOVMF_HW_SIZE (1 << IOVMF_SW_SHIFT) -+#define IOVMF_HW_MASK (IOVMF_HW_SIZE - 1) -+#define IOVMF_SW_MASK (~IOVMF_HW_MASK)UL -+ -+/* -+ * iovma: h/w flags derived from cam and ram attribute -+ */ -+#define IOVMF_CAM_MASK (~((1 << 10) - 1)) -+#define IOVMF_RAM_MASK (~IOVMF_CAM_MASK) -+ -+#define IOVMF_PGSZ_MASK (3 << 0) -+#define IOVMF_PGSZ_1M MMU_CAM_PGSZ_1M -+#define IOVMF_PGSZ_64K MMU_CAM_PGSZ_64K -+#define IOVMF_PGSZ_4K MMU_CAM_PGSZ_4K -+#define IOVMF_PGSZ_16M MMU_CAM_PGSZ_16M -+ -+#define IOVMF_ENDIAN_MASK (1 << 9) -+#define IOVMF_ENDIAN_BIG MMU_RAM_ENDIAN_BIG -+#define IOVMF_ENDIAN_LITTLE MMU_RAM_ENDIAN_LITTLE -+ -+#define IOVMF_ELSZ_MASK (3 << 7) -+#define IOVMF_ELSZ_8 MMU_RAM_ELSZ_8 -+#define IOVMF_ELSZ_16 MMU_RAM_ELSZ_16 -+#define IOVMF_ELSZ_32 MMU_RAM_ELSZ_32 -+#define IOVMF_ELSZ_NONE MMU_RAM_ELSZ_NONE -+ -+#define IOVMF_MIXED_MASK (1 << 6) -+#define IOVMF_MIXED MMU_RAM_MIXED -+ -+/* -+ * iovma: s/w flags, used for mapping and umapping internally. -+ */ -+#define IOVMF_MMIO (1 << IOVMF_SW_SHIFT) -+#define IOVMF_ALLOC (2 << IOVMF_SW_SHIFT) -+#define IOVMF_ALLOC_MASK (3 << IOVMF_SW_SHIFT) -+ -+/* "superpages" is supported just with physically linear pages */ -+#define IOVMF_DISCONT (1 << (2 + IOVMF_SW_SHIFT)) -+#define IOVMF_LINEAR (2 << (2 + IOVMF_SW_SHIFT)) -+#define IOVMF_LINEAR_MASK (3 << (2 + IOVMF_SW_SHIFT)) -+ -+#define IOVMF_DA_FIXED (1 << (4 + IOVMF_SW_SHIFT)) -+#define IOVMF_DA_ANON (2 << (4 + IOVMF_SW_SHIFT)) -+#define IOVMF_DA_MASK (3 << (4 + IOVMF_SW_SHIFT)) -+ -+ -+extern struct iovm_struct *find_iovm_area(struct iommu *obj, u32 da); -+extern u32 iommu_vmap(struct iommu *obj, u32 da, -+ const struct sg_table *sgt, u32 flags); -+extern struct sg_table *iommu_vunmap(struct iommu *obj, u32 da); -+extern u32 iommu_vmalloc(struct iommu *obj, u32 da, size_t bytes, -+ u32 flags); -+extern void iommu_vfree(struct iommu *obj, const u32 da); -+extern u32 iommu_kmap(struct iommu *obj, u32 da, u32 pa, size_t bytes, -+ u32 flags); -+extern void iommu_kunmap(struct iommu *obj, u32 da); -+extern u32 iommu_kmalloc(struct iommu *obj, u32 da, size_t bytes, -+ u32 flags); -+extern void iommu_kfree(struct iommu *obj, u32 da); -+ -+extern void *da_to_va(struct iommu *obj, u32 da); -+ -+#endif /* __IOMMU_MMAP_H */ -diff --git a/arch/arm/plat-omap/iovmm.c b/arch/arm/plat-omap/iovmm.c -new file mode 100644 -index 0000000..6726d10 ---- /dev/null -+++ b/arch/arm/plat-omap/iovmm.c -@@ -0,0 +1,891 @@ -+/* -+ * omap iommu: simple virtual address space management -+ * -+ * Copyright (C) 2008-2009 Nokia Corporation -+ * -+ * Written by Hiroshi DOYU <Hiroshi.DOYU@nokia.com> -+ * -+ * 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. -+ */ -+ -+#include <linux/err.h> -+#include <linux/vmalloc.h> -+#include <linux/device.h> -+#include <linux/scatterlist.h> -+ -+#include <asm/io.h> -+#include <asm/cacheflush.h> -+ -+#include <mach/iommu.h> -+#include <mach/iovmm.h> -+ -+#include "iopgtable.h" -+ -+/* -+ * A device driver needs to create address mappings between: -+ * -+ * - iommu/device address -+ * - physical address -+ * - mpu virtual address -+ * -+ * There are 4 possible patterns for them: -+ * -+ * |iova/ mapping iommu_ page -+ * | da pa va (d)-(p)-(v) function type -+ * --------------------------------------------------------------------------- -+ * 1 | c c c 1 - 1 - 1 _kmap() / _kunmap() s -+ * 2 | c c,a c 1 - 1 - 1 _kmalloc()/ _kfree() s -+ * 3 | c d c 1 - n - 1 _vmap() / _vunmap() s -+ * 4 | c d,a c 1 - n - 1 _vmalloc()/ _vfree() n* -+ * -+ * -+ * 'iova': device iommu virtual address -+ * 'da': alias of 'iova' -+ * 'pa': physical address -+ * 'va': mpu virtual address -+ * -+ * 'c': contiguous memory area -+ * 'd': dicontiguous memory area -+ * 'a': anonymous memory allocation -+ * '()': optional feature -+ * -+ * 'n': a normal page(4KB) size is used. -+ * 's': multiple iommu superpage(16MB, 1MB, 64KB, 4KB) size is used. -+ * -+ * '*': not yet, but feasible. -+ */ -+ -+static struct kmem_cache *iovm_area_cachep; -+ -+/* return total bytes of sg buffers */ -+static size_t sgtable_len(const struct sg_table *sgt) -+{ -+ unsigned int i, total = 0; -+ struct scatterlist *sg; -+ -+ if (!sgt) -+ return 0; -+ -+ for_each_sg(sgt->sgl, sg, sgt->nents, i) { -+ size_t bytes; -+ -+ bytes = sg_dma_len(sg); -+ -+ if (!iopgsz_ok(bytes)) { -+ pr_err("%s: sg[%d] not iommu pagesize(%x)\n", -+ __func__, i, bytes); -+ return 0; -+ } -+ -+ total += bytes; -+ } -+ -+ return total; -+} -+#define sgtable_ok(x) (!!sgtable_len(x)) -+ -+/* -+ * calculate the optimal number sg elements from total bytes based on -+ * iommu superpages -+ */ -+static unsigned int sgtable_nents(size_t bytes) -+{ -+ int i; -+ unsigned int nr_entries; -+ const unsigned long pagesize[] = { SZ_16M, SZ_1M, SZ_64K, SZ_4K, }; -+ -+ if (!IS_ALIGNED(bytes, PAGE_SIZE)) { -+ pr_err("%s: wrong size %08x\n", __func__, bytes); -+ return 0; -+ } -+ -+ nr_entries = 0; -+ for (i = 0; i < ARRAY_SIZE(pagesize); i++) { -+ if (bytes >= pagesize[i]) { -+ nr_entries += (bytes / pagesize[i]); -+ bytes %= pagesize[i]; -+ } -+ } -+ BUG_ON(bytes); -+ -+ return nr_entries; -+} -+ -+/* allocate and initialize sg_table header(a kind of 'superblock') */ -+static struct sg_table *sgtable_alloc(const size_t bytes, u32 flags) -+{ -+ unsigned int nr_entries; -+ int err; -+ struct sg_table *sgt; -+ -+ if (!bytes) -+ return ERR_PTR(-EINVAL); -+ -+ if (!IS_ALIGNED(bytes, PAGE_SIZE)) -+ return ERR_PTR(-EINVAL); -+ -+ /* FIXME: IOVMF_DA_FIXED should support 'superpages' */ -+ if ((flags & IOVMF_LINEAR) && (flags & IOVMF_DA_ANON)) { -+ nr_entries = sgtable_nents(bytes); -+ if (!nr_entries) -+ return ERR_PTR(-EINVAL); -+ } else -+ nr_entries = bytes / PAGE_SIZE; -+ -+ sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); -+ if (!sgt) -+ return ERR_PTR(-ENOMEM); -+ -+ err = sg_alloc_table(sgt, nr_entries, GFP_KERNEL); -+ if (err) -+ return ERR_PTR(err); -+ -+ pr_debug("%s: sgt:%p(%d entries)\n", __func__, sgt, nr_entries); -+ -+ return sgt; -+} -+ -+/* free sg_table header(a kind of superblock) */ -+static void sgtable_free(struct sg_table *sgt) -+{ -+ if (!sgt) -+ return; -+ -+ sg_free_table(sgt); -+ kfree(sgt); -+ -+ pr_debug("%s: sgt:%p\n", __func__, sgt); -+} -+ -+/* map 'sglist' to a contiguous mpu virtual area and return 'va' */ -+static void *vmap_sg(const struct sg_table *sgt) -+{ -+ u32 va; -+ size_t total; -+ unsigned int i; -+ struct scatterlist *sg; -+ struct vm_struct *new; -+ -+ total = sgtable_len(sgt); -+ if (!total) -+ return ERR_PTR(-EINVAL); -+ -+ new = __get_vm_area(total, VM_IOREMAP, VMALLOC_START, VMALLOC_END); -+ if (!new) -+ return ERR_PTR(-ENOMEM); -+ va = (u32)new->addr; -+ -+ for_each_sg(sgt->sgl, sg, sgt->nents, i) { -+ size_t bytes; -+ u32 pa; -+ int err; -+ -+ pa = sg_phys(sg); -+ bytes = sg_dma_len(sg); -+ -+ BUG_ON(bytes != PAGE_SIZE); -+ -+ err = ioremap_page(va, pa, MT_DEVICE); -+ if (err) -+ goto err_out; -+ -+ va += bytes; -+ } -+ -+ flush_cache_vmap(new->addr, total); -+ return new->addr; -+ -+err_out: -+ WARN_ON(1); /* FIXME: cleanup some mpu mappings */ -+ vunmap(new->addr); -+ return ERR_PTR(-EAGAIN); -+} -+ -+static inline void vunmap_sg(const void *va) -+{ -+ vunmap(va); -+} -+ -+static struct iovm_struct *__find_iovm_area(struct iommu *obj, const u32 da) -+{ -+ struct iovm_struct *tmp; -+ -+ list_for_each_entry(tmp, &obj->mmap, list) { -+ if ((da >= tmp->da_start) && (da < tmp->da_end)) { -+ size_t len; -+ -+ len = tmp->da_end - tmp->da_start; -+ -+ dev_dbg(obj->dev, "%s: %08x-%08x-%08x(%x) %08x\n", -+ __func__, tmp->da_start, da, tmp->da_end, len, -+ tmp->flags); -+ -+ return tmp; -+ } -+ } -+ -+ return NULL; -+} -+ -+/** -+ * find_iovm_area - find iovma which includes @da -+ * @da: iommu device virtual address -+ * -+ * Find the existing iovma starting at @da -+ */ -+struct iovm_struct *find_iovm_area(struct iommu *obj, u32 da) -+{ -+ struct iovm_struct *area; -+ -+ mutex_lock(&obj->mmap_lock); -+ area = __find_iovm_area(obj, da); -+ mutex_unlock(&obj->mmap_lock); -+ -+ return area; -+} -+EXPORT_SYMBOL_GPL(find_iovm_area); -+ -+/* -+ * This finds the hole(area) which fits the requested address and len -+ * in iovmas mmap, and returns the new allocated iovma. -+ */ -+static struct iovm_struct *alloc_iovm_area(struct iommu *obj, u32 da, -+ size_t bytes, u32 flags) -+{ -+ struct iovm_struct *new, *tmp; -+ u32 start, prev_end, alignement; -+ -+ if (!obj || !bytes) -+ return ERR_PTR(-EINVAL); -+ -+ start = da; -+ alignement = PAGE_SIZE; -+ -+ if (flags & IOVMF_DA_ANON) { -+ /* -+ * Reserve the first page for NULL -+ */ -+ start = PAGE_SIZE; -+ if (flags & IOVMF_LINEAR) -+ alignement = iopgsz_max(bytes); -+ start = roundup(start, alignement); -+ } -+ -+ tmp = NULL; -+ if (list_empty(&obj->mmap)) -+ goto found; -+ -+ prev_end = 0; -+ list_for_each_entry(tmp, &obj->mmap, list) { -+ -+ if ((prev_end <= start) && (start + bytes < tmp->da_start)) -+ goto found; -+ -+ if (flags & IOVMF_DA_ANON) -+ start = roundup(tmp->da_end, alignement); -+ -+ prev_end = tmp->da_end; -+ } -+ -+ if ((start >= prev_end) && (ULONG_MAX - start >= bytes)) -+ goto found; -+ -+ dev_dbg(obj->dev, "%s: no space to fit %08x(%x) flags: %08x\n", -+ __func__, da, bytes, flags); -+ -+ return ERR_PTR(-EINVAL); -+ -+found: -+ new = kmem_cache_zalloc(iovm_area_cachep, GFP_KERNEL); -+ if (!new) -+ return ERR_PTR(-ENOMEM); -+ -+ new->iommu = obj; -+ new->da_start = start; -+ new->da_end = start + bytes; -+ new->flags = flags; -+ -+ /* -+ * keep ascending order of iovmas -+ */ -+ if (tmp) -+ list_add_tail(&new->list, &tmp->list); -+ else -+ list_add(&new->list, &obj->mmap); -+ -+ dev_dbg(obj->dev, "%s: found %08x-%08x-%08x(%x) %08x\n", -+ __func__, new->da_start, start, new->da_end, bytes, flags); -+ -+ return new; -+} -+ -+static void free_iovm_area(struct iommu *obj, struct iovm_struct *area) -+{ -+ size_t bytes; -+ -+ BUG_ON(!obj || !area); -+ -+ bytes = area->da_end - area->da_start; -+ -+ dev_dbg(obj->dev, "%s: %08x-%08x(%x) %08x\n", -+ __func__, area->da_start, area->da_end, bytes, area->flags); -+ -+ list_del(&area->list); -+ kmem_cache_free(iovm_area_cachep, area); -+} -+ -+/** -+ * da_to_va - convert (d) to (v) -+ * @obj: objective iommu -+ * @da: iommu device virtual address -+ * @va: mpu virtual address -+ * -+ * Returns mpu virtual addr which corresponds to a given device virtual addr -+ */ -+void *da_to_va(struct iommu *obj, u32 da) -+{ -+ void *va = NULL; -+ struct iovm_struct *area; -+ -+ mutex_lock(&obj->mmap_lock); -+ -+ area = __find_iovm_area(obj, da); -+ if (!area) { -+ dev_warn(obj->dev, "%s: no da area(%08x)\n", __func__, da); -+ goto out; -+ } -+ va = area->va; -+ mutex_unlock(&obj->mmap_lock); -+out: -+ return va; -+} -+EXPORT_SYMBOL_GPL(da_to_va); -+ -+static void sgtable_fill_vmalloc(struct sg_table *sgt, void *_va) -+{ -+ unsigned int i; -+ struct scatterlist *sg; -+ void *va = _va; -+ void *va_end; -+ -+ for_each_sg(sgt->sgl, sg, sgt->nents, i) { -+ struct page *pg; -+ const size_t bytes = PAGE_SIZE; -+ -+ /* -+ * iommu 'superpage' isn't supported with 'iommu_vmalloc()' -+ */ -+ pg = vmalloc_to_page(va); -+ BUG_ON(!pg); -+ sg_set_page(sg, pg, bytes, 0); -+ -+ va += bytes; -+ } -+ -+ va_end = _va + PAGE_SIZE * i; -+ flush_cache_vmap(_va, va_end); -+} -+ -+static inline void sgtable_drain_vmalloc(struct sg_table *sgt) -+{ -+ /* -+ * Actually this is not necessary at all, just exists for -+ * consistency of the code readibility. -+ */ -+ BUG_ON(!sgt); -+} -+ -+static void sgtable_fill_kmalloc(struct sg_table *sgt, u32 pa, size_t len) -+{ -+ unsigned int i; -+ struct scatterlist *sg; -+ void *va; -+ -+ va = phys_to_virt(pa); -+ -+ for_each_sg(sgt->sgl, sg, sgt->nents, i) { -+ size_t bytes; -+ -+ bytes = iopgsz_max(len); -+ -+ BUG_ON(!iopgsz_ok(bytes)); -+ -+ sg_set_buf(sg, phys_to_virt(pa), bytes); -+ /* -+ * 'pa' is cotinuous(linear). -+ */ -+ pa += bytes; -+ len -= bytes; -+ } -+ BUG_ON(len); -+ -+ clean_dcache_area(va, len); -+} -+ -+static inline void sgtable_drain_kmalloc(struct sg_table *sgt) -+{ -+ /* -+ * Actually this is not necessary at all, just exists for -+ * consistency of the code readibility -+ */ -+ BUG_ON(!sgt); -+} -+ -+/* create 'da' <-> 'pa' mapping from 'sgt' */ -+static int map_iovm_area(struct iommu *obj, struct iovm_struct *new, -+ const struct sg_table *sgt, u32 flags) -+{ -+ int err; -+ unsigned int i, j; -+ struct scatterlist *sg; -+ u32 da = new->da_start; -+ -+ if (!obj || !new || !sgt) -+ return -EINVAL; -+ -+ BUG_ON(!sgtable_ok(sgt)); -+ -+ for_each_sg(sgt->sgl, sg, sgt->nents, i) { -+ u32 pa; -+ int pgsz; -+ size_t bytes; -+ struct iotlb_entry e; -+ -+ pa = sg_phys(sg); -+ bytes = sg_dma_len(sg); -+ -+ flags &= ~IOVMF_PGSZ_MASK; -+ pgsz = bytes_to_iopgsz(bytes); -+ if (pgsz < 0) -+ goto err_out; -+ flags |= pgsz; -+ -+ pr_debug("%s: [%d] %08x %08x(%x)\n", __func__, -+ i, da, pa, bytes); -+ -+ iotlb_init_entry(&e, da, pa, flags); -+ err = iopgtable_store_entry(obj, &e); -+ if (err) -+ goto err_out; -+ -+ da += bytes; -+ } -+ return 0; -+ -+err_out: -+ da = new->da_start; -+ -+ for_each_sg(sgt->sgl, sg, i, j) { -+ size_t bytes; -+ -+ bytes = iopgtable_clear_entry(obj, da); -+ -+ BUG_ON(!iopgsz_ok(bytes)); -+ -+ da += bytes; -+ } -+ return err; -+} -+ -+/* release 'da' <-> 'pa' mapping */ -+static void unmap_iovm_area(struct iommu *obj, struct iovm_struct *area) -+{ -+ u32 start; -+ size_t total = area->da_end - area->da_start; -+ -+ BUG_ON((!total) || !IS_ALIGNED(total, PAGE_SIZE)); -+ -+ start = area->da_start; -+ while (total > 0) { -+ size_t bytes; -+ -+ bytes = iopgtable_clear_entry(obj, start); -+ if (bytes == 0) -+ bytes = PAGE_SIZE; -+ else -+ dev_dbg(obj->dev, "%s: unmap %08x(%x) %08x\n", -+ __func__, start, bytes, area->flags); -+ -+ BUG_ON(!IS_ALIGNED(bytes, PAGE_SIZE)); -+ -+ total -= bytes; -+ start += bytes; -+ } -+ BUG_ON(total); -+} -+ -+/* template function for all unmapping */ -+static struct sg_table *unmap_vm_area(struct iommu *obj, const u32 da, -+ void (*fn)(const void *), u32 flags) -+{ -+ struct sg_table *sgt = NULL; -+ struct iovm_struct *area; -+ -+ BUG_ON(in_interrupt()); -+ -+ if (!IS_ALIGNED(da, PAGE_SIZE)) { -+ dev_err(obj->dev, "%s: alignment err(%08x)\n", __func__, da); -+ return NULL; -+ } -+ -+ mutex_lock(&obj->mmap_lock); -+ -+ area = __find_iovm_area(obj, da); -+ if (!area) { -+ dev_err(obj->dev, "%s: no da area(%08x)\n", __func__, da); -+ goto out; -+ } -+ -+ if ((area->flags & flags) != flags) { -+ dev_err(obj->dev, "%s: wrong flags(%08x)\n", __func__, -+ area->flags); -+ goto out; -+ } -+ sgt = (struct sg_table *)area->sgt; -+ -+ unmap_iovm_area(obj, area); -+ -+ fn(area->va); -+ -+ dev_dbg(obj->dev, "%s: %08x-%08x-%08x(%x) %08x\n", __func__, -+ area->da_start, da, area->da_end, -+ area->da_end - area->da_start, area->flags); -+ -+ free_iovm_area(obj, area); -+out: -+ mutex_unlock(&obj->mmap_lock); -+ -+ return sgt; -+} -+ -+static u32 map_iommu_region(struct iommu *obj, u32 da, -+ const struct sg_table *sgt, void *va, size_t bytes, u32 flags) -+{ -+ int err = -ENOMEM; -+ struct iovm_struct *new; -+ -+ mutex_lock(&obj->mmap_lock); -+ -+ new = alloc_iovm_area(obj, da, bytes, flags); -+ if (IS_ERR(new)) { -+ err = PTR_ERR(new); -+ goto err_alloc_iovma; -+ } -+ new->va = va; -+ new->sgt = sgt; -+ -+ if (map_iovm_area(obj, new, sgt, new->flags)) -+ goto err_map; -+ -+ mutex_unlock(&obj->mmap_lock); -+ -+ dev_dbg(obj->dev, "%s: da:%08x(%x) flags:%08x va:%p\n", -+ __func__, new->da_start, bytes, new->flags, va); -+ -+ return new->da_start; -+ -+err_map: -+ free_iovm_area(obj, new); -+err_alloc_iovma: -+ mutex_unlock(&obj->mmap_lock); -+ return err; -+} -+ -+static inline u32 __iommu_vmap(struct iommu *obj, u32 da, -+ const struct sg_table *sgt, void *va, size_t bytes, u32 flags) -+{ -+ return map_iommu_region(obj, da, sgt, va, bytes, flags); -+} -+ -+/** -+ * iommu_vmap - (d)-(p)-(v) address mapper -+ * @obj: objective iommu -+ * @sgt: address of scatter gather table -+ * @flags: iovma and page property -+ * -+ * Creates 1-n-1 mapping with given @sgt and returns @da. -+ * All @sgt element must be io page size aligned. -+ */ -+u32 iommu_vmap(struct iommu *obj, u32 da, const struct sg_table *sgt, -+ u32 flags) -+{ -+ size_t bytes; -+ void *va; -+ -+ if (!obj || !obj->dev || !sgt) -+ return -EINVAL; -+ -+ bytes = sgtable_len(sgt); -+ if (!bytes) -+ return -EINVAL; -+ bytes = PAGE_ALIGN(bytes); -+ -+ va = vmap_sg(sgt); -+ if (IS_ERR(va)) -+ return PTR_ERR(va); -+ -+ flags &= IOVMF_HW_MASK; -+ flags |= IOVMF_DISCONT; -+ flags |= IOVMF_MMIO; -+ flags |= (da ? IOVMF_DA_FIXED : IOVMF_DA_ANON); -+ -+ da = __iommu_vmap(obj, da, sgt, va, bytes, flags); -+ if (IS_ERR_VALUE(da)) -+ vunmap_sg(va); -+ -+ return da; -+} -+EXPORT_SYMBOL_GPL(iommu_vmap); -+ -+/** -+ * iommu_vunmap - release virtual mapping obtained by 'iommu_vmap()' -+ * @obj: objective iommu -+ * @da: iommu device virtual address -+ * -+ * Free the iommu virtually contiguous memory area starting at -+ * @da, which was returned by 'iommu_vmap()'. -+ */ -+struct sg_table *iommu_vunmap(struct iommu *obj, u32 da) -+{ -+ struct sg_table *sgt; -+ /* -+ * 'sgt' is allocated before 'iommu_vmalloc()' is called. -+ * Just returns 'sgt' to the caller to free -+ */ -+ sgt = unmap_vm_area(obj, da, vunmap_sg, IOVMF_DISCONT | IOVMF_MMIO); -+ if (!sgt) -+ dev_err(obj->dev, "%s: No sgt\n", __func__); -+ return sgt; -+} -+EXPORT_SYMBOL_GPL(iommu_vunmap); -+ -+/** -+ * iommu_vmalloc - (d)-(p)-(v) address allocator and mapper -+ * @obj: objective iommu -+ * @da: contiguous iommu virtual memory -+ * @bytes: allocation size -+ * @flags: iovma and page property -+ * -+ * Allocate @bytes linearly and creates 1-n-1 mapping and returns -+ * @da again, which might be adjusted if 'IOVMF_DA_ANON' is set. -+ */ -+u32 iommu_vmalloc(struct iommu *obj, u32 da, size_t bytes, u32 flags) -+{ -+ void *va; -+ struct sg_table *sgt; -+ -+ if (!obj || !obj->dev || !bytes) -+ return -EINVAL; -+ -+ bytes = PAGE_ALIGN(bytes); -+ -+ va = vmalloc(bytes); -+ if (!va) -+ return -ENOMEM; -+ -+ sgt = sgtable_alloc(bytes, flags); -+ if (IS_ERR(sgt)) { -+ da = PTR_ERR(sgt); -+ goto err_sgt_alloc; -+ } -+ sgtable_fill_vmalloc(sgt, va); -+ -+ flags &= IOVMF_HW_MASK; -+ flags |= IOVMF_DISCONT; -+ flags |= IOVMF_ALLOC; -+ flags |= (da ? IOVMF_DA_FIXED : IOVMF_DA_ANON); -+ -+ da = __iommu_vmap(obj, da, sgt, va, bytes, flags); -+ if (IS_ERR_VALUE(da)) -+ goto err_iommu_vmap; -+ -+ return da; -+ -+err_iommu_vmap: -+ sgtable_drain_vmalloc(sgt); -+ sgtable_free(sgt); -+err_sgt_alloc: -+ vfree(va); -+ return da; -+} -+EXPORT_SYMBOL_GPL(iommu_vmalloc); -+ -+/** -+ * iommu_vfree - release memory allocated by 'iommu_vmalloc()' -+ * @obj: objective iommu -+ * @da: iommu device virtual address -+ * -+ * Frees the iommu virtually continuous memory area starting at -+ * @da, as obtained from 'iommu_vmalloc()'. -+ */ -+void iommu_vfree(struct iommu *obj, const u32 da) -+{ -+ struct sg_table *sgt; -+ -+ sgt = unmap_vm_area(obj, da, vfree, IOVMF_DISCONT | IOVMF_ALLOC); -+ if (!sgt) -+ dev_err(obj->dev, "%s: No sgt\n", __func__); -+ sgtable_free(sgt); -+} -+EXPORT_SYMBOL_GPL(iommu_vfree); -+ -+static u32 __iommu_kmap(struct iommu *obj, u32 da, u32 pa, void *va, -+ size_t bytes, u32 flags) -+{ -+ struct sg_table *sgt; -+ -+ sgt = sgtable_alloc(bytes, flags); -+ if (IS_ERR(sgt)) -+ return PTR_ERR(sgt); -+ -+ sgtable_fill_kmalloc(sgt, pa, bytes); -+ -+ da = map_iommu_region(obj, da, sgt, va, bytes, flags); -+ if (IS_ERR_VALUE(da)) { -+ sgtable_drain_kmalloc(sgt); -+ sgtable_free(sgt); -+ } -+ -+ return da; -+} -+ -+/** -+ * iommu_kmap - (d)-(p)-(v) address mapper -+ * @obj: objective iommu -+ * @da: contiguous iommu virtual memory -+ * @pa: contiguous physical memory -+ * @flags: iovma and page property -+ * -+ * Creates 1-1-1 mapping and returns @da again, which can be -+ * adjusted if 'IOVMF_DA_ANON' is set. -+ */ -+u32 iommu_kmap(struct iommu *obj, u32 da, u32 pa, size_t bytes, -+ u32 flags) -+{ -+ void *va; -+ -+ if (!obj || !obj->dev || !bytes) -+ return -EINVAL; -+ -+ bytes = PAGE_ALIGN(bytes); -+ -+ va = ioremap(pa, bytes); -+ if (!va) -+ return -ENOMEM; -+ -+ flags &= IOVMF_HW_MASK; -+ flags |= IOVMF_LINEAR; -+ flags |= IOVMF_MMIO; -+ flags |= (da ? IOVMF_DA_FIXED : IOVMF_DA_ANON); -+ -+ da = __iommu_kmap(obj, da, pa, va, bytes, flags); -+ if (IS_ERR_VALUE(da)) -+ iounmap(va); -+ -+ return da; -+} -+EXPORT_SYMBOL_GPL(iommu_kmap); -+ -+/** -+ * iommu_kunmap - release virtual mapping obtained by 'iommu_kmap()' -+ * @obj: objective iommu -+ * @da: iommu device virtual address -+ * -+ * Frees the iommu virtually contiguous memory area starting at -+ * @da, which was passed to and was returned by'iommu_kmap()'. -+ */ -+void iommu_kunmap(struct iommu *obj, u32 da) -+{ -+ struct sg_table *sgt; -+ -+ sgt = unmap_vm_area(obj, da, __iounmap, IOVMF_LINEAR | IOVMF_MMIO); -+ if (!sgt) -+ dev_err(obj->dev, "%s: No sgt\n", __func__); -+ sgtable_free(sgt); -+} -+EXPORT_SYMBOL_GPL(iommu_kunmap); -+ -+/** -+ * iommu_kmalloc - (d)-(p)-(v) address allocator and mapper -+ * @obj: objective iommu -+ * @da: contiguous iommu virtual memory -+ * @bytes: bytes for allocation -+ * @flags: iovma and page property -+ * -+ * Allocate @bytes linearly and creates 1-1-1 mapping and returns -+ * @da again, which might be adjusted if 'IOVMF_DA_ANON' is set. -+ */ -+u32 iommu_kmalloc(struct iommu *obj, u32 da, size_t bytes, u32 flags) -+{ -+ void *va; -+ u32 pa; -+ -+ if (!obj || !obj->dev || !bytes) -+ return -EINVAL; -+ -+ bytes = PAGE_ALIGN(bytes); -+ -+ va = kmalloc(bytes, GFP_KERNEL | GFP_DMA); -+ if (!va) -+ return -ENOMEM; -+ pa = virt_to_phys(va); -+ -+ flags &= IOVMF_HW_MASK; -+ flags |= IOVMF_LINEAR; -+ flags |= IOVMF_ALLOC; -+ flags |= (da ? IOVMF_DA_FIXED : IOVMF_DA_ANON); -+ -+ da = __iommu_kmap(obj, da, pa, va, bytes, flags); -+ if (IS_ERR_VALUE(da)) -+ kfree(va); -+ -+ return da; -+} -+EXPORT_SYMBOL_GPL(iommu_kmalloc); -+ -+/** -+ * iommu_kfree - release virtual mapping obtained by 'iommu_kmalloc()' -+ * @obj: objective iommu -+ * @da: iommu device virtual address -+ * -+ * Frees the iommu virtually contiguous memory area starting at -+ * @da, which was passed to and was returned by'iommu_kmalloc()'. -+ */ -+void iommu_kfree(struct iommu *obj, u32 da) -+{ -+ struct sg_table *sgt; -+ -+ sgt = unmap_vm_area(obj, da, kfree, IOVMF_LINEAR | IOVMF_ALLOC); -+ if (!sgt) -+ dev_err(obj->dev, "%s: No sgt\n", __func__); -+ sgtable_free(sgt); -+} -+EXPORT_SYMBOL_GPL(iommu_kfree); -+ -+ -+static int __init iovmm_init(void) -+{ -+ const unsigned long flags = SLAB_HWCACHE_ALIGN; -+ struct kmem_cache *p; -+ -+ p = kmem_cache_create("iovm_area_cache", sizeof(struct iovm_struct), 0, -+ flags, NULL); -+ if (!p) -+ return -ENOMEM; -+ iovm_area_cachep = p; -+ -+ return 0; -+} -+module_init(iovmm_init); -+ -+static void __exit iovmm_exit(void) -+{ -+ kmem_cache_destroy(iovm_area_cachep); -+} -+module_exit(iovmm_exit); -+ -+MODULE_DESCRIPTION("omap iommu: simple virtual address space management"); -+MODULE_AUTHOR("Hiroshi DOYU <Hiroshi.DOYU@nokia.com>"); -+MODULE_LICENSE("GPL v2"); --- -1.5.6.5 - |