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