msm: kgsl: Change iommu structure to better represent the hardware

Change the iommu structures to properly represent the iommu hardware
which makes the code more readable.

Change-Id: I61cf988998da3fc4f62ca020e4240cf50ddd20f0
Signed-off-by: Shubhraprakash Das <sadas@codeaurora.org>
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index 21f14ac..e2a945f 100644
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -16,32 +16,13 @@
 #include <linux/genalloc.h>
 #include <linux/slab.h>
 #include <linux/iommu.h>
-#include <mach/iommu.h>
 #include <linux/msm_kgsl.h>
 
 #include "kgsl.h"
 #include "kgsl_device.h"
 #include "kgsl_mmu.h"
 #include "kgsl_sharedmem.h"
-
-/*
- * On APQ8064, KGSL can control a maximum of 4 IOMMU devices: 2 user and 2
- * priv domains, 1 each for each of the AXI ports attached to the GPU.  8660
- * and 8960 have only one AXI port, so maximum allowable IOMMU devices for those
- * chips is 2.
- */
-
-#define KGSL_IOMMU_MAX_DEV 4
-
-struct kgsl_iommu_device {
-	struct device *dev;
-	int attached;
-};
-
-struct kgsl_iommu {
-	struct kgsl_iommu_device dev[KGSL_IOMMU_MAX_DEV];
-	int dev_count;
-};
+#include "kgsl_iommu.h"
 
 static int kgsl_iommu_pt_equal(struct kgsl_pagetable *pt,
 					unsigned int pt_base)
@@ -66,103 +47,181 @@
 	return domain;
 }
 
+/*
+ * kgsl_detach_pagetable_iommu_domain - Detach the IOMMU unit from a
+ * pagetable
+ * @mmu - Pointer to the device mmu structure
+ * @priv - Flag indicating whether the private or user context is to be
+ * detached
+ *
+ * Detach the IOMMU unit with the domain that is contained in the
+ * hwpagetable of the given mmu. After detaching the IOMMU unit is not
+ * in use because the PTBR will not be set after a detach
+ * Return - void
+ */
 static void kgsl_detach_pagetable_iommu_domain(struct kgsl_mmu *mmu)
 {
 	struct iommu_domain *domain;
 	struct kgsl_iommu *iommu = mmu->priv;
-	int i;
+	int i, j;
 
 	BUG_ON(mmu->hwpagetable == NULL);
 	BUG_ON(mmu->hwpagetable->priv == NULL);
 
 	domain = mmu->hwpagetable->priv;
 
-	for (i = 0; i < iommu->dev_count; i++) {
-		iommu_detach_device(domain, iommu->dev[i].dev);
-		iommu->dev[i].attached = 0;
-		KGSL_MEM_INFO(mmu->device,
-			"iommu %p detached from user dev of MMU: %p\n",
-			domain, mmu);
+	for (i = 0; i < iommu->unit_count; i++) {
+		struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
+		for (j = 0; j < iommu_unit->dev_count; j++) {
+			if (iommu_unit->dev[j].attached) {
+				iommu_detach_device(domain,
+						iommu_unit->dev[j].dev);
+				iommu_unit->dev[j].attached = false;
+				KGSL_MEM_INFO(mmu->device, "iommu %p detached "
+					"from user dev of MMU: %p\n",
+					domain, mmu);
+			}
+		}
 	}
 }
 
+/*
+ * kgsl_attach_pagetable_iommu_domain - Attach the IOMMU unit to a
+ * pagetable, i.e set the IOMMU's PTBR to the pagetable address and
+ * setup other IOMMU registers for the device so that it becomes
+ * active
+ * @mmu - Pointer to the device mmu structure
+ * @priv - Flag indicating whether the private or user context is to be
+ * attached
+ *
+ * Attach the IOMMU unit with the domain that is contained in the
+ * hwpagetable of the given mmu.
+ * Return - 0 on success else error code
+ */
 static int kgsl_attach_pagetable_iommu_domain(struct kgsl_mmu *mmu)
 {
 	struct iommu_domain *domain;
 	struct kgsl_iommu *iommu = mmu->priv;
-	int i, ret = 0;
+	int i, j, ret = 0;
 
 	BUG_ON(mmu->hwpagetable == NULL);
 	BUG_ON(mmu->hwpagetable->priv == NULL);
 
 	domain = mmu->hwpagetable->priv;
 
-	for (i = 0; i < iommu->dev_count; i++) {
-		if (iommu->dev[i].attached == 0) {
-			ret = iommu_attach_device(domain, iommu->dev[i].dev);
-			if (ret) {
-				KGSL_MEM_ERR(mmu->device,
-					"Failed to attach device, err %d\n",
+	/*
+	 * Loop through all the iommu devcies under all iommu units and
+	 * attach the domain
+	 */
+	for (i = 0; i < iommu->unit_count; i++) {
+		struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[i];
+		for (j = 0; j < iommu_unit->dev_count; j++) {
+			if (!iommu_unit->dev[j].attached) {
+				ret = iommu_attach_device(domain,
+							iommu_unit->dev[j].dev);
+				if (ret) {
+					KGSL_MEM_ERR(mmu->device,
+						"Failed to attach device, err %d\n",
 						ret);
-				goto done;
+					goto done;
+				}
+				iommu_unit->dev[j].attached = true;
+				KGSL_MEM_INFO(mmu->device,
+				"iommu pt %p attached to dev %p, ctx_id %d\n",
+					domain, iommu_unit->dev[j].dev,
+					iommu_unit->dev[j].ctx_id);
 			}
-
-			iommu->dev[i].attached = 1;
-			KGSL_MEM_INFO(mmu->device,
-				"iommu %p detached from user dev of MMU: %p\n",
-				domain, mmu);
 		}
 	}
-
 done:
 	return ret;
 }
 
-static int _get_iommu_ctxs(struct kgsl_iommu *iommu, struct kgsl_device *device,
-	struct kgsl_device_iommu_data *data)
+/*
+ * _get_iommu_ctxs - Get device pointer to IOMMU contexts
+ * @mmu - Pointer to mmu device
+ * data - Pointer to the platform data containing information about
+ * iommu devices for one iommu unit
+ * unit_id - The IOMMU unit number. This is not a specific ID but just
+ * a serial number. The serial numbers are treated as ID's of the
+ * IOMMU units
+ *
+ * Return - 0 on success else error code
+ */
+static int _get_iommu_ctxs(struct kgsl_mmu *mmu,
+	struct kgsl_device_iommu_data *data, unsigned int unit_id)
 {
+	struct kgsl_iommu *iommu = mmu->priv;
+	struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[unit_id];
 	int i;
 
-	for (i = 0; i < data->iommu_ctx_count; i++) {
-		if (iommu->dev_count >= KGSL_IOMMU_MAX_DEV) {
-			KGSL_CORE_ERR("Tried to attach too many IOMMU "
-				"devices\n");
-			return -ENOMEM;
-		}
+	if (data->iommu_ctx_count > KGSL_IOMMU_MAX_DEVS_PER_UNIT) {
+		KGSL_CORE_ERR("Too many iommu devices defined for an "
+				"IOMMU unit\n");
+		return -EINVAL;
+	}
 
-		if (!data->iommu_ctx_names[i])
+	for (i = 0; i < data->iommu_ctx_count; i++) {
+		if (!data->iommu_ctxs[i].iommu_ctx_name)
 			continue;
 
-		iommu->dev[iommu->dev_count].dev =
-			msm_iommu_get_ctx(data->iommu_ctx_names[i]);
-		if (iommu->dev[iommu->dev_count].dev == NULL) {
-			KGSL_CORE_ERR("Failed to iommu dev handle for "
-				"device %s\n", data->iommu_ctx_names[i]);
+		iommu_unit->dev[iommu_unit->dev_count].dev =
+			msm_iommu_get_ctx(data->iommu_ctxs[i].iommu_ctx_name);
+		if (iommu_unit->dev[iommu_unit->dev_count].dev == NULL) {
+			KGSL_CORE_ERR("Failed to get iommu dev handle for "
+			"device %s\n", data->iommu_ctxs[i].iommu_ctx_name);
 			return -EINVAL;
 		}
+		if (KGSL_IOMMU_CONTEXT_USER != data->iommu_ctxs[i].ctx_id &&
+			KGSL_IOMMU_CONTEXT_PRIV != data->iommu_ctxs[i].ctx_id) {
+			KGSL_CORE_ERR("Invalid context ID defined: %d\n",
+					data->iommu_ctxs[i].ctx_id);
+			return -EINVAL;
+		}
+		iommu_unit->dev[iommu_unit->dev_count].ctx_id =
+						data->iommu_ctxs[i].ctx_id;
+		KGSL_DRV_INFO(mmu->device,
+				"Obtained dev handle %p for iommu context %s\n",
+				iommu_unit->dev[iommu_unit->dev_count].dev,
+				data->iommu_ctxs[i].iommu_ctx_name);
 
-		iommu->dev_count++;
+		iommu_unit->dev_count++;
 	}
 
 	return 0;
 }
 
-static int kgsl_get_iommu_ctxt(struct kgsl_iommu *iommu,
-				struct kgsl_device *device)
+/*
+ * kgsl_get_iommu_ctxt - Get device pointer to IOMMU contexts
+ * @mmu - Pointer to mmu device
+ *
+ * Get the device pointers for the IOMMU user and priv contexts of the
+ * kgsl device
+ * Return - 0 on success else error code
+ */
+static int kgsl_get_iommu_ctxt(struct kgsl_mmu *mmu)
 {
-	struct kgsl_device_platform_data *pdata =
-		kgsl_device_get_drvdata(device);
+	struct platform_device *pdev =
+		container_of(mmu->device->parentdev, struct platform_device,
+				dev);
+	struct kgsl_device_platform_data *pdata_dev = pdev->dev.platform_data;
+	struct kgsl_iommu *iommu = mmu->device->mmu.priv;
 	int i, ret = 0;
 
-	/* Go through the IOMMU data and attach all the domains */
+	/* Go through the IOMMU data and get all the context devices */
+	if (KGSL_IOMMU_MAX_UNITS < pdata_dev->iommu_count) {
+		KGSL_CORE_ERR("Too many IOMMU units defined\n");
+		ret = -EINVAL;
+		goto  done;
+	}
 
-	for (i = 0; i < pdata->iommu_count; i++) {
-		ret = _get_iommu_ctxs(iommu, device,
-			&pdata->iommu_data[i]);
+	for (i = 0; i < pdata_dev->iommu_count; i++) {
+		ret = _get_iommu_ctxs(mmu, &pdata_dev->iommu_data[i], i);
 		if (ret)
 			break;
 	}
-
+	iommu->unit_count = pdata_dev->iommu_count;
+done:
 	return ret;
 }
 
@@ -200,15 +259,18 @@
 		return -ENOMEM;
 	}
 
-	status = kgsl_get_iommu_ctxt(iommu, mmu->device);
-	if (status) {
-		kfree(iommu);
-		iommu = NULL;
-	}
 	mmu->priv = iommu;
+	status = kgsl_get_iommu_ctxt(mmu);
+	if (status)
+		goto done;
 
 	dev_info(mmu->device->dev, "|%s| MMU type set for device is IOMMU\n",
 			__func__);
+done:
+	if (status) {
+		kfree(iommu);
+		mmu->priv = NULL;
+	}
 	return status;
 }
 
@@ -229,8 +291,12 @@
 	mmu->hwpagetable = mmu->defaultpagetable;
 
 	status = kgsl_attach_pagetable_iommu_domain(mmu);
-	if (!status)
+	if (!status) {
 		mmu->flags |= KGSL_FLAGS_STARTED;
+	} else {
+		kgsl_detach_pagetable_iommu_domain(mmu);
+		mmu->hwpagetable = NULL;
+	}
 
 	return status;
 }
@@ -309,6 +375,7 @@
 	if (mmu->flags & KGSL_FLAGS_STARTED) {
 		/* detach iommu attachment */
 		kgsl_detach_pagetable_iommu_domain(mmu);
+		mmu->hwpagetable = NULL;
 
 		mmu->flags &= ~KGSL_FLAGS_STARTED;
 	}
diff --git a/drivers/gpu/msm/kgsl_iommu.h b/drivers/gpu/msm/kgsl_iommu.h
new file mode 100644
index 0000000..e2033c5
--- /dev/null
+++ b/drivers/gpu/msm/kgsl_iommu.h
@@ -0,0 +1,84 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+#ifndef __KGSL_IOMMU_H
+#define __KGSL_IOMMU_H
+
+#include <mach/iommu.h>
+
+/*
+ * Max number of iommu units that the gpu core can have
+ * On APQ8064, KGSL can control a maximum of 2 IOMMU units.
+ */
+#define KGSL_IOMMU_MAX_UNITS 2
+
+/* Max number of iommu contexts per IOMMU unit */
+#define KGSL_IOMMU_MAX_DEVS_PER_UNIT 2
+
+/*
+ * struct kgsl_iommu_device - Structure holding data about iommu contexts
+ * @dev: Device pointer to iommu context
+ * @attached: Indicates whether this iommu context is presently attached to
+ * a pagetable/domain or not
+ * @pt_lsb: The LSB of IOMMU_TTBR0 register which is the pagetable
+ * register
+ * @ctx_id: This iommu units context id. It can be either 0 or 1
+ * @clk_enabled: If set indicates that iommu clocks of this iommu context
+ * are on, else the clocks are off
+ */
+struct kgsl_iommu_device {
+	struct device *dev;
+	bool attached;
+	unsigned int pt_lsb;
+	enum kgsl_iommu_context_id ctx_id;
+	bool clk_enabled;
+};
+
+/*
+ * struct kgsl_iommu_unit - Structure holding data about iommu units. An IOMMU
+ * units is basically a separte IOMMU h/w block with it's own IOMMU contexts
+ * @dev: Pointer to array of struct kgsl_iommu_device which has information
+ * about the IOMMU contexts under this IOMMU unit
+ * @dev_count: Number of IOMMU contexts that are valid in the previous feild
+ * @reg_map: Memory descriptor which holds the mapped address of this IOMMU
+ * units register range
+ */
+struct kgsl_iommu_unit {
+	struct kgsl_iommu_device dev[KGSL_IOMMU_MAX_DEVS_PER_UNIT];
+	unsigned int dev_count;
+	struct kgsl_memdesc reg_map;
+};
+
+/*
+ * struct kgsl_iommu - Structure holding iommu data for kgsl driver
+ * @dev: Array of kgsl_iommu_device which contain information about
+ * iommu contexts owned by graphics cores
+ * @unit_count: Number of IOMMU units that are available for this
+ * instance of the IOMMU driver
+ * @iommu_last_cmd_ts: The timestamp of last command submitted that
+ * aceeses iommu registers
+ * @device: Pointer to kgsl device
+ * @asids: A bit structure indicating which id's are presently used
+ * @asid: Contains the initial value of IOMMU_CONTEXTIDR when a domain
+ * is first attached
+ */
+struct kgsl_iommu {
+	struct kgsl_iommu_unit iommu_units[KGSL_IOMMU_MAX_UNITS];
+	unsigned int unit_count;
+	unsigned int iommu_last_cmd_ts;
+	struct kgsl_device *device;
+	unsigned long *asids;
+	unsigned int asid;
+	unsigned int active_ctx;
+};
+
+#endif