msm: qdss: make qdss_clk_enable/disable external functions

Move qdss_clk_enable/disable declarations to the external qdss header file.
Add reference counting and mutex locking to make them useable by clients.

Change-Id: If8a5bd9783138af1f9d620e78ecbd0163ff25a6b
Signed-off-by: Pratik Patel <pratikp@codeaurora.org>
diff --git a/arch/arm/mach-msm/qdss.c b/arch/arm/mach-msm/qdss.c
index 53a2436..80ec65a 100644
--- a/arch/arm/mach-msm/qdss.c
+++ b/arch/arm/mach-msm/qdss.c
@@ -18,6 +18,7 @@
 #include <linux/platform_device.h>
 #include <linux/io.h>
 #include <linux/err.h>
+#include <linux/export.h>
 #include <mach/rpm.h>
 
 #include "rpm_resources.h"
@@ -32,6 +33,8 @@
 struct qdss_ctx {
 	struct kobject	*modulekobj;
 	uint8_t		max_clk;
+	uint8_t		clk_count;
+	struct mutex	clk_mutex;
 };
 
 static struct qdss_ctx qdss;
@@ -42,35 +45,71 @@
 	return qdss.modulekobj;
 }
 
+/**
+ * qdss_clk_enable - enable qdss clocks
+ *
+ * Enables qdss clocks via RPM if they aren't already enabled, otherwise
+ * increments the reference count.
+ *
+ * CONTEXT:
+ * Might sleep. Uses a mutex lock. Should be called from a non-atomic context.
+ *
+ * RETURNS:
+ * 0 on success, non-zero on failure
+ */
 int qdss_clk_enable(void)
 {
 	int ret;
-
 	struct msm_rpm_iv_pair iv;
-	iv.id = MSM_RPM_ID_QDSS_CLK;
-	if (qdss.max_clk)
-		iv.value = QDSS_CLK_ON_HSDBG;
-	else
-		iv.value = QDSS_CLK_ON_DBG;
-	ret = msm_rpmrs_set(MSM_RPM_CTX_SET_0, &iv, 1);
-	if (WARN(ret, "qdss clks not enabled (%d)\n", ret))
-		goto err_clk;
 
+	mutex_lock(&qdss.clk_mutex);
+	if (qdss.clk_count == 0) {
+		iv.id = MSM_RPM_ID_QDSS_CLK;
+		if (qdss.max_clk)
+			iv.value = QDSS_CLK_ON_HSDBG;
+		else
+			iv.value = QDSS_CLK_ON_DBG;
+		ret = msm_rpmrs_set(MSM_RPM_CTX_SET_0, &iv, 1);
+		if (WARN(ret, "qdss clks not enabled (%d)\n", ret))
+			goto err_clk;
+	}
+	qdss.clk_count++;
+	mutex_unlock(&qdss.clk_mutex);
 	return 0;
 err_clk:
+	mutex_unlock(&qdss.clk_mutex);
 	return ret;
 }
+EXPORT_SYMBOL(qdss_clk_enable);
 
+/**
+ * qdss_clk_disable - disable qdss clocks
+ *
+ * Disables qdss clocks via RPM if the reference count is one, otherwise
+ * decrements the reference count.
+ *
+ * CONTEXT:
+ * Might sleep. Uses a mutex lock. Should be called from a non-atomic context.
+ */
 void qdss_clk_disable(void)
 {
 	int ret;
 	struct msm_rpm_iv_pair iv;
 
-	iv.id = MSM_RPM_ID_QDSS_CLK;
-	iv.value = QDSS_CLK_OFF;
-	ret = msm_rpmrs_set(MSM_RPM_CTX_SET_0, &iv, 1);
-	WARN(ret, "qdss clks not disabled (%d)\n", ret);
+	mutex_lock(&qdss.clk_mutex);
+	if (WARN(qdss.clk_count == 0, "qdss clks are unbalanced\n"))
+		goto out;
+	if (qdss.clk_count == 1) {
+		iv.id = MSM_RPM_ID_QDSS_CLK;
+		iv.value = QDSS_CLK_OFF;
+		ret = msm_rpmrs_set(MSM_RPM_CTX_SET_0, &iv, 1);
+		WARN(ret, "qdss clks not disabled (%d)\n", ret);
+	}
+	qdss.clk_count--;
+out:
+	mutex_unlock(&qdss.clk_mutex);
 }
+EXPORT_SYMBOL(qdss_clk_disable);
 
 #define QDSS_ATTR(name)						\
 static struct kobj_attribute name##_attr =				\
@@ -128,6 +167,8 @@
 {
 	int ret;
 
+	mutex_init(&qdss.clk_mutex);
+
 	ret = qdss_sysfs_init();
 	if (ret)
 		goto err_sysfs;
@@ -155,6 +196,7 @@
 err_etb:
 	qdss_sysfs_exit();
 err_sysfs:
+	mutex_destroy(&qdss.clk_mutex);
 	pr_err("QDSS init failed\n");
 	return ret;
 }
@@ -167,6 +209,7 @@
 	funnel_exit();
 	tpiu_exit();
 	etb_exit();
+	mutex_destroy(&qdss.clk_mutex);
 }
 module_exit(qdss_exit);