iommu: msm: Register PMU with IOMMUv0 driver

Register Performance Monitor Unit (PMU) driver with the iommu driver.

Change-Id: I194e035b392000a20c64ab6243d9aa231540e011
Signed-off-by: Olav Haugan <ohaugan@codeaurora.org>
diff --git a/drivers/iommu/msm_iommu-v0.c b/drivers/iommu/msm_iommu-v0.c
index 28f1516..6bf0220 100644
--- a/drivers/iommu/msm_iommu-v0.c
+++ b/drivers/iommu/msm_iommu-v0.c
@@ -27,6 +27,7 @@
 #include <asm/cacheflush.h>
 #include <asm/sizes.h>
 
+#include <mach/iommu_perfmon.h>
 #include <mach/iommu_hw-v0.h>
 #include <mach/iommu.h>
 #include <mach/msm_smsm.h>
@@ -160,6 +161,41 @@
 	clk_disable_unprepare(drvdata->pclk);
 }
 
+static int _iommu_power_on(void *data)
+{
+	struct msm_iommu_drvdata *drvdata;
+
+	drvdata = (struct msm_iommu_drvdata *)data;
+	return __enable_clocks(drvdata);
+}
+
+static int _iommu_power_off(void *data)
+{
+	struct msm_iommu_drvdata *drvdata;
+
+	drvdata = (struct msm_iommu_drvdata *)data;
+	__disable_clocks(drvdata);
+	return 0;
+}
+
+static void _iommu_lock_acquire(void)
+{
+	msm_iommu_lock();
+}
+
+static void _iommu_lock_release(void)
+{
+	msm_iommu_unlock();
+}
+
+struct iommu_access_ops iommu_access_ops_v0 = {
+	.iommu_power_on = _iommu_power_on,
+	.iommu_power_off = _iommu_power_off,
+	.iommu_lock_acquire = _iommu_lock_acquire,
+	.iommu_lock_release = _iommu_lock_release,
+};
+EXPORT_SYMBOL(iommu_access_ops_v0);
+
 static int __flush_iotlb_va(struct iommu_domain *domain, unsigned int va)
 {
 	struct msm_priv *priv = domain->priv;
@@ -468,6 +504,11 @@
 	list_add(&(ctx_drvdata->attached_elm), &priv->list_attached);
 
 	ctx_drvdata->attached_domain = domain;
+
+	mutex_unlock(&msm_iommu_lock);
+
+	msm_iommu_attached(dev->parent);
+	return ret;
 unlock:
 	mutex_unlock(&msm_iommu_lock);
 	return ret;
@@ -481,6 +522,8 @@
 	struct msm_iommu_ctx_drvdata *ctx_drvdata;
 	int ret;
 
+	msm_iommu_detached(dev->parent);
+
 	mutex_lock(&msm_iommu_lock);
 	priv = domain->priv;
 
diff --git a/drivers/iommu/msm_iommu_dev-v0.c b/drivers/iommu/msm_iommu_dev-v0.c
index 176a57e..681d7b2 100644
--- a/drivers/iommu/msm_iommu_dev-v0.c
+++ b/drivers/iommu/msm_iommu_dev-v0.c
@@ -27,6 +27,7 @@
 #include <linux/of_address.h>
 #include <linux/of_device.h>
 
+#include <mach/iommu_perfmon.h>
 #include <mach/iommu_hw-v0.h>
 #include <mach/iommu.h>
 
@@ -261,8 +262,68 @@
 	return ret;
 }
 
+static int msm_iommu_pmon_parse_dt(struct platform_device *pdev,
+					struct iommu_pmon *pmon_info)
+{
+	int ret = 0;
+	int irq = platform_get_irq(pdev, 0);
+	unsigned int cls_prop_size;
+
+	if (irq > 0) {
+		pmon_info->iommu.evt_irq = platform_get_irq(pdev, 0);
+
+		ret = of_property_read_u32(pdev->dev.of_node,
+					   "qcom,iommu-pmu-ngroups",
+					   &pmon_info->num_groups);
+		if (ret) {
+			pr_err("Error reading qcom,iommu-pmu-ngroups\n");
+			goto fail;
+		}
+		ret = of_property_read_u32(pdev->dev.of_node,
+					   "qcom,iommu-pmu-ncounters",
+					   &pmon_info->num_counters);
+		if (ret) {
+			pr_err("Error reading qcom,iommu-pmu-ncounters\n");
+			goto fail;
+		}
+
+		if (!of_get_property(pdev->dev.of_node,
+				     "qcom,iommu-pmu-event-classes",
+				     &cls_prop_size)) {
+			pr_err("Error reading qcom,iommu-pmu-event-classes\n");
+			return -EINVAL;
+		}
+
+		pmon_info->event_cls_supported =
+			   devm_kzalloc(&pdev->dev, cls_prop_size, GFP_KERNEL);
+
+		if (!pmon_info->event_cls_supported) {
+			pr_err("Unable to get memory for event class array\n");
+			return -ENOMEM;
+		}
+
+		pmon_info->nevent_cls_supported = cls_prop_size / sizeof(u32);
+
+		ret = of_property_read_u32_array(pdev->dev.of_node,
+					"qcom,iommu-pmu-event-classes",
+					pmon_info->event_cls_supported,
+					pmon_info->nevent_cls_supported);
+		if (ret) {
+			pr_err("Error reading qcom,iommu-pmu-event-classes\n");
+			return ret;
+		}
+	} else {
+		pmon_info->iommu.evt_irq = -1;
+		ret = irq;
+	}
+
+fail:
+	return ret;
+}
+
 static int msm_iommu_probe(struct platform_device *pdev)
 {
+	struct iommu_pmon *pmon_info;
 	struct msm_iommu_drvdata *drvdata;
 	struct msm_iommu_dev *iommu_dev = pdev->dev.platform_data;
 	int ret;
@@ -345,6 +406,29 @@
 
 	__disable_clocks(drvdata);
 
+	pmon_info = msm_iommu_pm_alloc(&pdev->dev);
+	if (pmon_info != NULL) {
+		ret = msm_iommu_pmon_parse_dt(pdev, pmon_info);
+		if (ret) {
+			msm_iommu_pm_free(&pdev->dev);
+			pr_info("%s: pmon not available.\n", drvdata->name);
+		} else {
+			pmon_info->iommu.base = drvdata->base;
+			pmon_info->iommu.ops = &iommu_access_ops_v0;
+			pmon_info->iommu.hw_ops = iommu_pm_get_hw_ops_v0();
+			pmon_info->iommu.iommu_name = drvdata->name;
+			ret = msm_iommu_pm_iommu_register(pmon_info);
+			if (ret) {
+				pr_err("%s iommu register fail\n",
+								drvdata->name);
+				msm_iommu_pm_free(&pdev->dev);
+			} else {
+				pr_debug("%s iommu registered for pmon\n",
+						pmon_info->iommu.iommu_name);
+			}
+		}
+	}
+
 	return 0;
 
 fail_clk: