msm: coresight: Add coresight support for bus profiling

Add ability to collect bus profiling data by registering
with Coresight module.

Change-Id: Ia4015c3c4adff3e296638f89368b988d50ee5d27
Signed-off-by: Gagan Mac <gmac@codeaurora.org>
diff --git a/arch/arm/mach-msm/msm_bus/Makefile b/arch/arm/mach-msm/msm_bus/Makefile
index d435e2b..e785c00 100644
--- a/arch/arm/mach-msm/msm_bus/Makefile
+++ b/arch/arm/mach-msm/msm_bus/Makefile
@@ -3,6 +3,7 @@
 #
 obj-y += msm_bus_core.o msm_bus_fabric.o msm_bus_config.o msm_bus_arb.o
 obj-y += msm_bus_bimc.o msm_bus_noc.o
+obj-$(CONFIG_CORESIGHT) +=  msm_buspm_coresight.o
 obj-$(CONFIG_OF) += msm_bus_of.o
 obj-$(CONFIG_MSM_RPM) += msm_bus_rpm.o
 obj-$(CONFIG_MSM_RPM_SMD) += msm_bus_rpm_smd.o
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_core.h b/arch/arm/mach-msm/msm_bus/msm_bus_core.h
index 8c3e3d3..20380cf 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_core.h
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_core.h
@@ -261,6 +261,21 @@
 }
 #endif
 
+#ifdef CONFIG_CORESIGHT
+int msmbus_coresight_init(struct platform_device *pdev);
+void msmbus_coresight_remove(struct platform_device *pdev);
+#else
+int msmbus_coresight_init(struct platform_device *pdev)
+{
+	return 0;
+}
+
+void msmbus_coresight_remove(struct platform_device *pdev)
+{
+}
+#endif
+
+
 #ifdef CONFIG_OF
 struct msm_bus_fabric_registration
 	*msm_bus_of_get_fab_data(struct platform_device *pdev);
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c b/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c
index dd00aad..0d28e9d 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c
@@ -789,6 +789,9 @@
 		}
 	}
 
+	if (msmbus_coresight_init(pdev))
+		pr_warn("Coresight support absent for bus: %d\n", pdata->id);
+
 	return ret;
 err:
 	kfree(fabric->info.node_info);
@@ -804,6 +807,7 @@
 	int ret = 0;
 
 	fabdev = platform_get_drvdata(pdev);
+	msmbus_coresight_remove(pdev);
 	msm_bus_fabric_device_unregister(fabdev);
 	fabric = to_msm_bus_fabric(fabdev);
 	msm_bus_dbg_commit_data(fabric->fabdev.name, NULL, 0, 0, 0,
diff --git a/arch/arm/mach-msm/msm_bus/msm_buspm_coresight.c b/arch/arm/mach-msm/msm_bus/msm_buspm_coresight.c
new file mode 100644
index 0000000..6dbe954
--- /dev/null
+++ b/arch/arm/mach-msm/msm_bus/msm_buspm_coresight.c
@@ -0,0 +1,159 @@
+/* Copyright (c) 2013, The Linux Foundation. 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.
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/of_coresight.h>
+#include <linux/coresight.h>
+#include <linux/memory_alloc.h>
+#include <linux/io.h>
+#include <linux/of.h>
+
+struct msmbus_coresight_drvdata {
+	struct device			*dev;
+	struct coresight_device		*csdev;
+	struct clk			*clk;
+	const char			*clk_name;
+	const char			*clknode;
+};
+
+static int msmbus_coresight_enable(struct coresight_device *csdev)
+{
+	struct msmbus_coresight_drvdata *drvdata =
+		dev_get_drvdata(csdev->dev.parent);
+
+	return clk_prepare_enable(drvdata->clk);
+}
+
+static void msmbus_coresight_disable(struct coresight_device *csdev)
+{
+	struct msmbus_coresight_drvdata *drvdata =
+		dev_get_drvdata(csdev->dev.parent);
+
+	clk_disable_unprepare(drvdata->clk);
+}
+
+static const struct coresight_ops_source msmbus_coresight_source_ops = {
+	.enable		= msmbus_coresight_enable,
+	.disable	= msmbus_coresight_disable,
+};
+
+static const struct coresight_ops msmbus_coresight_cs_ops = {
+	.source_ops	= &msmbus_coresight_source_ops,
+};
+
+void msmbus_coresight_remove(struct platform_device *pdev)
+{
+	struct msmbus_coresight_drvdata *drvdata = platform_get_drvdata(pdev);
+
+	msmbus_coresight_disable(drvdata->csdev);
+	coresight_unregister(drvdata->csdev);
+	devm_kfree(&pdev->dev, drvdata);
+	platform_set_drvdata(pdev, NULL);
+}
+EXPORT_SYMBOL(msmbus_coresight_remove);
+
+static int buspm_of_get_clk(struct device_node *of_node,
+	struct msmbus_coresight_drvdata *drvdata)
+{
+	if (of_property_read_string(of_node, "qcom,fabclk-dual",
+						&drvdata->clk_name)) {
+		pr_err("Error: Unable to find clock from of_node\n");
+		return -EINVAL;
+	}
+
+	if (of_property_read_string(of_node, "label", &drvdata->clknode)) {
+		pr_err("Error: Unable to find clock-node from of_node\n");
+		return -EINVAL;
+	}
+
+	drvdata->clk = clk_get_sys(drvdata->clknode, drvdata->clk_name);
+	if (IS_ERR(drvdata->clk)) {
+		pr_err("Error: clk_get_sys failed for: %s\n",
+			drvdata->clknode);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int msmbus_coresight_init(struct platform_device *pdev)
+{
+	int ret;
+	struct device *dev = &pdev->dev;
+	struct coresight_platform_data *pdata;
+	struct msmbus_coresight_drvdata *drvdata;
+	struct coresight_desc *desc;
+
+	if (pdev->dev.of_node) {
+		pdata = of_get_coresight_platform_data(dev, pdev->dev.of_node);
+		if (IS_ERR(pdata))
+			return PTR_ERR(pdata);
+		pdev->dev.platform_data = pdata;
+	}
+
+	drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
+	if (!drvdata) {
+		pr_err("coresight: Alloc for drvdata failed\n");
+		return -ENOMEM;
+	}
+
+	drvdata->dev = &pdev->dev;
+	platform_set_drvdata(pdev, drvdata);
+	ret = buspm_of_get_clk(pdev->dev.of_node, drvdata);
+	if (ret) {
+		pr_err("Error getting clocks\n");
+		ret = -ENXIO;
+		goto err1;
+	}
+
+	desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
+	if (!desc) {
+		pr_err("coresight: Error allocating memory\n");
+		ret = -ENOMEM;
+		goto err1;
+	}
+
+	desc->type = CORESIGHT_DEV_TYPE_SOURCE;
+	desc->subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_BUS;
+	desc->ops = &msmbus_coresight_cs_ops;
+	desc->pdata = pdev->dev.platform_data;
+	desc->dev = &pdev->dev;
+	desc->owner = THIS_MODULE;
+	drvdata->csdev = coresight_register(desc);
+	if (IS_ERR(drvdata->csdev)) {
+		pr_err("coresight: Coresight register failed\n");
+		ret = PTR_ERR(drvdata->csdev);
+		goto err0;
+	}
+
+	dev_info(dev, "msmbus_coresight initialized\n");
+
+	return 0;
+err0:
+	devm_kfree(dev, desc);
+err1:
+	devm_kfree(dev, drvdata);
+	platform_set_drvdata(pdev, NULL);
+	return ret;
+}
+EXPORT_SYMBOL(msmbus_coresight_init);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MSM BusPM CoreSight Driver");