From 14e8ccf940e799702236287b4fd956924feb4aad Mon Sep 17 00:00:00 2001 From: Janghyuck Kim Date: Wed, 11 May 2016 11:25:18 +0900 Subject: [PATCH] [COMMON] iommu/exynos: add dma-window property parsing Each domain may need different dma ranges for client device's requirement. 'dma-window' property specifies dma ranges for each domain. This property is optional, so if it doesn't exist, default dma ranges from 0x10000000 to 0xD0000000 are used. If this property is used, address and size cells should be described. Below is an example. iommu-domain_something { compatible = "samsung,exynos-iommu-bus"; #dma-address-cells = <1>; #dma-size-cells = <1>; /* start address, size */ dma-window = <0x10000000 0xC0000000>; domain-clients = <&something>; }; Change-Id: I2765f8a4a6b142475c5de80aa73234bef0bd3248 Signed-off-by: Janghyuck Kim --- drivers/iommu/exynos-iommu.c | 26 +++++++++++++++++++++++++- drivers/iommu/exynos-iommu.h | 6 ++++-- drivers/iommu/exynos-iovmm.c | 16 +++++++--------- 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 9c55c160fa5a..509a44477179 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -30,6 +30,11 @@ #include "exynos-iommu.h" +/* Default IOVA region: [0x1000000, 0xD0000000) */ +#define IOVA_START 0x10000000 +#define IOVA_END 0xD0000000 +#define IOVA_OVFL(x) ((x) > 0xFFFFFFFF) + static struct kmem_cache *lv2table_kmem_cache; static struct sysmmu_drvdata *sysmmu_drvdata_list; @@ -1253,11 +1258,30 @@ static int __init exynos_iommu_create_domain(void) struct device_node *np; struct exynos_iovmm *vmm = NULL; struct exynos_iommu_domain *domain; + unsigned int start = IOVA_START, end = IOVA_END; + dma_addr_t d_addr; + size_t d_size; int i = 0; + ret = of_get_dma_window(domain_np, NULL, 0, NULL, &d_addr, &d_size); + if (!ret) { + if (d_addr == 0 || IOVA_OVFL(d_addr + d_size)) { + pr_err("Failed to get valid dma ranges,\n"); + pr_err("Domain %s, range %pad++%#zx]\n", + domain_np->name, &d_addr, d_size); + of_node_put(domain_np); + return -EINVAL; + } + start = d_addr; + end = d_addr + d_size; + } + pr_info("DMA ranges for domain %s. [%#x..%#x]\n", + domain_np->name, start, end); + while ((np = of_parse_phandle(domain_np, "domain-clients", i++))) { if (!vmm) { - vmm = exynos_create_single_iovmm(np->name); + vmm = exynos_create_single_iovmm(np->name, + start, end); if (IS_ERR(vmm)) { pr_err("%s: Failed to create IOVM space\ of %s\n", diff --git a/drivers/iommu/exynos-iommu.h b/drivers/iommu/exynos-iommu.h index 3eb267ac85dd..955e66c7cfe9 100644 --- a/drivers/iommu/exynos-iommu.h +++ b/drivers/iommu/exynos-iommu.h @@ -363,7 +363,8 @@ static inline struct exynos_iovmm *exynos_get_iovmm(struct device *dev) struct exynos_vm_region *find_iovm_region(struct exynos_iovmm *vmm, dma_addr_t iova); -struct exynos_iovmm *exynos_create_single_iovmm(const char *name); +struct exynos_iovmm *exynos_create_single_iovmm(const char *name, + unsigned int start, unsigned int end); #else static inline struct exynos_iovmm *exynos_get_iovmm(struct device *dev) { @@ -376,7 +377,8 @@ struct exynos_vm_region *find_iovm_region(struct exynos_iovmm *vmm, return NULL; } -static inline struct exynos_iovmm *exynos_create_single_iovmm(const char *name) +static inline struct exynos_iovmm *exynos_create_single_iovmm(const char *name, + unsigned int start, unsigned int end); { return NULL; } diff --git a/drivers/iommu/exynos-iovmm.c b/drivers/iommu/exynos-iovmm.c index 3f8643937b97..b61ad53f9a6b 100644 --- a/drivers/iommu/exynos-iovmm.c +++ b/drivers/iommu/exynos-iovmm.c @@ -24,9 +24,6 @@ #include "exynos-iommu.h" -/* IOVM region: [0x1000000, 0xD0000000) */ -#define IOVA_START 0x10000000 -#define IOVM_SIZE (0xD0000000 - IOVA_START) #define sg_physically_continuous(sg) (sg_next(sg) == NULL) /* alloc_iovm_region - Allocate IO virtual memory region @@ -678,7 +675,8 @@ static void iovmm_register_debugfs(struct exynos_iovmm *vmm) exynos_iovmm_debugfs_root, vmm, &iovmm_debug_fops); } -struct exynos_iovmm *exynos_create_single_iovmm(const char *name) +struct exynos_iovmm *exynos_create_single_iovmm(const char *name, + unsigned int start, unsigned int end) { struct exynos_iovmm *vmm; int ret = 0; @@ -689,9 +687,9 @@ struct exynos_iovmm *exynos_create_single_iovmm(const char *name) goto err_alloc_vmm; } - vmm->iovm_size = IOVM_SIZE; - vmm->iova_start = IOVA_START; - vmm->vm_map = kzalloc(IOVM_BITMAP_SIZE(IOVM_SIZE), GFP_KERNEL); + vmm->iovm_size = (size_t)(end - start); + vmm->iova_start = start; + vmm->vm_map = kzalloc(IOVM_BITMAP_SIZE(vmm->iovm_size), GFP_KERNEL); if (!vmm->vm_map) { ret = -ENOMEM; goto err_setup_domain; @@ -712,8 +710,8 @@ struct exynos_iovmm *exynos_create_single_iovmm(const char *name) iovmm_register_debugfs(vmm); - pr_debug("%s IOVMM: Created %#x B IOVMM from %#x.\n", - name, IOVM_SIZE, IOVA_START); + pr_debug("%s IOVMM: Created %#zx B IOVMM from %#x.\n", + name, vmm->iovm_size, vmm->iova_start); return vmm; err_setup_domain: -- 2.20.1