[v3,0/2] ARM: IOMMU: tegra: Add iommu_ops for GART/SMMU driver

Submitted by Hiroshi Doyu on Jan. 11, 2012, 2:24 p.m.

Details

Message ID 20120111.162440.1854857348878391112.hdoyu@nvidia.com
State New
Headers show

Commit Message

Hiroshi Doyu Jan. 11, 2012, 2:24 p.m.
From: Russell King - ARM Linux <linux@arm.linux.org.uk>
Subject: Re: [PATCH v3 0/2] ARM: IOMMU: tegra: Add iommu_ops for GART/SMMU driver
Date: Thu, 5 Jan 2012 15:46:31 +0100
Message-ID: <20120105144631.GV11810@n2100.arm.linux.org.uk>

> On Thu, Jan 05, 2012 at 03:29:30PM +0100, Hiroshi Doyu wrote:
> > Ok, the above function order should be as below?
> > 
> >   page = alloc_page(GFP_KERNEL);
> >       cpu_addr = kmap(page);
> >       memset(cpu_addr, 0xa5, PAGE_SIZE);
> >   	dma_addr = dma_map_page(dev, page, 0, PAGE_SIZE, DMA_TO_DEVICE);
> >   	    < expect GPU does something >
> >   	dma_unmap_page(dev, dma_addr, PAGE_SIZE, DMA_TO_DEVICE);
> >       < CPU can access data here again>
> >       kunmap(cpu_addr);
> >   __free_page(page);
> 
> That should work, but it's not particularly nice to wrap kmap around
> the DMA API.
> 
> Bear in mind that alloc_page(GFP_KERNEL) will always give you a lowmem
> page.  So, you might as well do:
> 
> 	page = alloc_page(GFP_KERNEL);
> 	cpu_addr = page_address(page);
> 	memset()
> 	dma_addr = dma_map_single(dev, cpu_addr, PAGE_SIZE, DMA_TO_DEVICE);
> 		< GPU access >
> 	dma_unmap_single(dev, dma_ddr, PAGE_SIZE, DMA_TO_DEVICE);
> 	< CPU access >
> 	__free_page(page);
> 
> and this doesn't raise any issues with kmap vs DMA API.

Attached the update one just in case that someone wants to use it for theirs.

Patch hide | download patch | download mbox

From 76aefea1c7ad004590295dbe63c27f0c6f482f7a Mon Sep 17 00:00:00 2001
From: Hiroshi DOYU <hdoyu@nvidia.com>
Date: Thu, 15 Dec 2011 13:24:24 +0200
Subject: [PATCH 1/1] [NOT-FOR-MERGE] TEST: Simple dma-iommu-mapping API TEST
 module

This is not posted to merge but this is necessary to understand how
SMMU/GART works with DMA(-iommu-mapping-)API.

This is a test to verify DMA API(DMA iommu mapping API), where SoC
specific iommu_ops is used internally.

This does alloc/(un)map/free, but there's no actual access from device
side since it requires device specific communications.

Signed-off-by: Hiroshi DOYU <hdoyu@nvidia.com>
---
 drivers/iommu/dmaapi-test.c |  202 +++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 202 insertions(+), 0 deletions(-)
 create mode 100644 drivers/iommu/dmaapi-test.c

diff --git a/drivers/iommu/dmaapi-test.c b/drivers/iommu/dmaapi-test.c
new file mode 100644
index 0000000..3199386
--- /dev/null
+++ b/drivers/iommu/dmaapi-test.c
@@ -0,0 +1,202 @@ 
+/*
+ * DMA IOMMU mapping API test module
+ *
+ * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * Author: Krishna Reddy <vdumpa@nvidia.com>,
+ *	Hiroshi DOYU <hdoyu@nvidia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define DEBUG
+#define pr_fmt(fmt)	KBUILD_MODNAME ":" fmt
+
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/kthread.h>
+#include <linux/delay.h>
+#include <linux/highmem.h>
+#include <linux/pfn.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+
+#include <mach/iomap.h>
+#include <mach/smmu.h>
+
+#include <asm/dma-iommu.h>
+
+#include "devices.h"
+
+/* FIXME: 2x and 3x should be supported at once */
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+#define IOVA_START	TEGRA_GART_BASE
+#define IOVA_SIZE	TEGRA_GART_SIZE
+static struct platform_device *tegra_iommu_device = &tegra_gart_device;
+#elif CONFIG_ARCH_TEGRA_3x_SOC
+#define IOVA_START	TEGRA_SMMU_BASE
+#define IOVA_SIZE	TEGRA_SMMU_SIZE
+static struct platform_device *tegra_iommu_device = &tegra_smmu_device;
+#else
+#error Unsupported device
+#endif
+
+#define NUM_TEST	3
+#define MAP_SIZE	(4 * PAGE_SIZE)
+
+struct dmaapi_test_case {
+	char *name;
+	void (*fn)(struct device *);
+};
+
+static void dmaapi_test_map_page(struct device *dev)
+{
+	struct page *page;
+	dma_addr_t dma_addr;
+	void *cpu_addr;
+
+	page = alloc_page(GFP_KERNEL);
+	BUG_ON(!page);
+
+	cpu_addr = page_address(page);
+	BUG_ON(!cpu_addr);
+	memset(cpu_addr, 0xa5, PAGE_SIZE);
+
+	dma_addr = dma_map_page(dev, page, 0, PAGE_SIZE, DMA_TO_DEVICE);
+	BUG_ON(!dma_addr);
+
+	pr_debug("pid:%d,%s mapped\t%08x:%08x\n",
+		 current->pid, dev_name(dev), dma_addr, page_to_phys(page));
+
+	/*
+	 * Expect GPU access
+	 */
+
+	dma_unmap_page(dev, dma_addr, PAGE_SIZE, DMA_TO_DEVICE);
+
+	/*
+	 * CPU access
+	 */
+	BUG_ON(*(char *)cpu_addr != 0xa5);
+
+	__free_page(page);
+}
+
+static void dmaapi_test_alloc_coherent(struct device *dev)
+{
+	dma_addr_t da[NUM_TEST];
+	void *va[NUM_TEST];
+	int i;
+
+	for (i = 0; i < NUM_TEST; i++) {
+		void *cpu_addr;
+		dma_addr_t dma_addr;
+
+		cpu_addr = dma_alloc_coherent(dev, MAP_SIZE,
+					      &dma_addr, GFP_KERNEL);
+		BUG_ON(!cpu_addr);
+		memset(cpu_addr, 0xa5, MAP_SIZE);
+
+		pr_debug("pid:%d,%s,[%d] mapped\t%08x:%08x\n",
+			 current->pid, dev_name(dev), i,
+			 dma_addr, virt_to_phys(cpu_addr));
+
+		da[i] = dma_addr;
+		va[i] = cpu_addr;
+	}
+
+	while (--i >= 0) {
+		pr_debug("pid:%d,%s,[%d] unmapping\t%08x:%08x\n",
+			 current->pid, dev_name(dev), i,
+			 da[i], virt_to_phys(va[i]));
+		dma_free_coherent(dev, MAP_SIZE, va[i], da[i]);
+	}
+}
+
+static struct dmaapi_test_case test[] = {
+	{
+		.name = "dmaapi/map page",
+		.fn = dmaapi_test_map_page,
+	},
+	{
+		.name = "dmaapi/alloc coherent",
+		.fn = dmaapi_test_alloc_coherent,
+	},
+};
+
+static u32 dummy_hwgrp_map[] = {
+	HWG_DC | HWG_AFI | HWG_AVPC | HWG_DCB,
+	HWG_EPP | HWG_HC | HWG_G2 | HWG_MPE | HWG_HDA | HWG_ISP,
+	HWG_NV | HWG_PPCS | HWG_SATA | HWG_NV2 | HWG_VI | HWG_VDE,
+};
+
+/* FIXME: Need driver for iommu context? */
+static struct platform_device dmaapi_dummy_device[] = {
+	{ .name = "hwgrp@a", .id = -1, },
+	{ .name = "hwgrp@b", .id = -1, },
+	{ .name = "hwgrp@c", .id = -1, },
+};
+
+static int dmaapi_test_thread(void *data)
+{
+	int i;
+	struct dmaapi_test_case *c = data;
+
+	for (i = 0; true; i++) {
+		struct device *dev;
+		int interval[] = {7, 3, 5,};
+		int n;
+
+		n = i % ARRAY_SIZE(dmaapi_dummy_device);
+		ssleep(interval[n]);
+		dev = &dmaapi_dummy_device[n].dev;
+		c->fn(dev);
+	}
+	return 0;
+}
+
+static int __init dmaapi_test_init(void)
+{
+	int i;
+	struct dma_iommu_mapping *map;
+
+	map = arm_iommu_create_mapping(IOVA_START, IOVA_SIZE, 0);
+	BUG_ON(!map);
+	pr_debug("Allocate IOVA: %08x-%08x\n", map->base, map->base + IOVA_SIZE);
+
+	for (i = 0; i < ARRAY_SIZE(dmaapi_dummy_device); i++) {
+		int err;
+		struct platform_device *pdev = &dmaapi_dummy_device[i];
+
+		pdev->dev.platform_data = (void *)dummy_hwgrp_map[i];
+		pdev->dev.parent = &tegra_iommu_device->dev;
+		err = platform_device_register(pdev);
+		BUG_ON(err);
+
+		err = arm_iommu_attach_device(&pdev->dev, map);
+		BUG_ON(err);
+		pr_debug("IOMMU API: Attached to %s\n", dev_name(&pdev->dev));
+	}
+
+	for (i = 0; i < ARRAY_SIZE(test); i++)
+		kthread_run(dmaapi_test_thread, &test[i], test[i].name);
+
+	return 0;
+}
+module_init(dmaapi_test_init);
+
+MODULE_AUTHOR("Krishna Reddy <vdumpa@nvidia.com>");
+MODULE_AUTHOR("Hiroshi DOYU <hdoyu@nvidia.com>");
+MODULE_DESCRIPTION("DMA IOMMU mapping API test");
+MODULE_LICENSE("GPL v2");
-- 
1.7.5.4