Perf: Add L1 counters to tracepoints

Create a new tracepoint to get L1 PMU counter
information at every context switch. Counters
are captured only when the tracepoint is enabled
via perf or ftrace.

Change-Id: If05527b29e04c700170cfdf9f88d300b2f558ffa
Signed-off-by: Ashwin Chaugule <ashwinc@codeaurora.org>
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index d07b094..dc8b675 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -24,6 +24,7 @@
 endif
 
 obj-y += acpuclock.o
+obj-$(CONFIG_HW_PERF_EVENTS) += perf_trace_counters.o
 obj-$(CONFIG_ARCH_MSM_KRAIT) += acpuclock-krait.o
 ifdef CONFIG_ARCH_MSM_KRAIT
 obj-$(CONFIG_DEBUG_FS) += acpuclock-krait-debug.o
diff --git a/arch/arm/mach-msm/perf_debug.c b/arch/arm/mach-msm/perf_debug.c
index 96122de..5bf88a2 100644
--- a/arch/arm/mach-msm/perf_debug.c
+++ b/arch/arm/mach-msm/perf_debug.c
@@ -29,6 +29,7 @@
 	"4  Perf: Check perf activity on correct CPU\n"
 	"5  Perf: Add DT support for L1 and L2 PMU\n"
 	"6  Perf: Add cortex A5 device tree support\n"
+	"7  Perf: Add L1 counters to tracepoints\n"
 ;
 
 static ssize_t desc_read(struct file *fp, char __user *buf,
diff --git a/arch/arm/mach-msm/perf_trace_counters.c b/arch/arm/mach-msm/perf_trace_counters.c
new file mode 100644
index 0000000..d961994
--- /dev/null
+++ b/arch/arm/mach-msm/perf_trace_counters.c
@@ -0,0 +1,42 @@
+/* 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 <asm/thread_notify.h>
+#define CREATE_TRACE_POINTS
+#include "perf_trace_counters.h"
+
+static int tracectr_notifier(struct notifier_block *self, unsigned long cmd,
+		void *v)
+{
+	static int old_pid = -1;
+	struct thread_info *thread = v;
+	int current_pid;
+
+	if (cmd != THREAD_NOTIFY_SWITCH)
+		return old_pid;
+
+	current_pid = thread->task->pid;
+	if (old_pid != -1)
+		trace_sched_switch_with_ctrs(old_pid, current_pid);
+	old_pid = current_pid;
+	return old_pid;
+}
+
+static struct notifier_block tracectr_notifier_block = {
+	.notifier_call  = tracectr_notifier,
+};
+
+int __init init_tracecounters(void)
+{
+	thread_register_notifier(&tracectr_notifier_block);
+	return 0;
+}
+late_initcall(init_tracecounters);
diff --git a/arch/arm/mach-msm/perf_trace_counters.h b/arch/arm/mach-msm/perf_trace_counters.h
new file mode 100644
index 0000000..ce7e336
--- /dev/null
+++ b/arch/arm/mach-msm/perf_trace_counters.h
@@ -0,0 +1,127 @@
+/* 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.
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM perf_trace_counters
+
+#if !defined(_PERF_TRACE_COUNTERS_H_) || defined(TRACE_HEADER_MULTI_READ)
+#define _PERF_TRACE_COUNTERS_H_
+
+/* Ctr index for PMCNTENSET/CLR */
+#define CC 0x80000000
+#define C0 0x1
+#define C1 0x10
+#define C2 0x100
+#define C3 0x1000
+
+
+#include <linux/sched.h>
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(sched_switch_with_ctrs,
+
+		TP_PROTO(pid_t prev, pid_t next),
+
+		TP_ARGS(prev, next),
+
+		TP_STRUCT__entry(
+			__field(pid_t,	old_pid)
+			__field(pid_t,	new_pid)
+			__field(u32, cctr)
+			__field(u32, ctr0)
+			__field(u32, ctr1)
+			__field(u32, ctr2)
+			__field(u32, ctr3)
+		),
+
+		TP_fast_assign(
+			__entry->old_pid	= prev;
+			__entry->new_pid	= next;
+
+			/* cycle counter */
+			/* Disable */
+			asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r"(CC));
+			/* Read value */
+			asm volatile("mrc p15, 0, %0, c9, c13, 0"
+				: "=r"(__entry->cctr));
+			/* Reset */
+			asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r"(0));
+			/* Enable */
+			asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r"(CC));
+
+			/* ctr 0 */
+			/* Disable */
+			asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r"(C0));
+			/* Select */
+			asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r"(0));
+			/* Read value */
+			asm volatile("mrc p15, 0, %0, c9, c13, 2"
+					: "=r"(__entry->ctr0));
+			/* Reset */
+			asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r"(0));
+			/* Enable */
+			asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r"(C0));
+
+			/* ctr 1 */
+			/* Disable */
+			asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r"(C1));
+			/* Select */
+			asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r"(1));
+			/* Read value */
+			asm volatile("mrc p15, 0, %0, c9, c13, 2"
+					: "=r"(__entry->ctr1));
+			/* Reset */
+			asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r"(0));
+			/* Enable */
+			asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r"(C1));
+
+			/* ctr 2 */
+			/* Disable */
+			asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r"(C2));
+			/* Select */
+			asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r"(2));
+			/* Read value */
+			asm volatile("mrc p15, 0, %0, c9, c13, 2"
+					: "=r"(__entry->ctr2));
+			/* Reset */
+			asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r"(0));
+			/* Enable */
+			asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r"(C2));
+
+			/* ctr 3 */
+			/* Disable */
+			asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r"(C3));
+			/* Select */
+			asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r"(3));
+			/* Read value */
+			asm volatile("mrc p15, 0, %0, c9, c13, 2"
+					: "=r"(__entry->ctr3));
+			/* Reset */
+			asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r"(0));
+			/* Enable */
+			asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r"(C3));
+
+		),
+
+		TP_printk("prev_pid=%d, next_pid=%d, CCNTR: %u, CTR0: %u," \
+				" CTR1: %u, CTR2: %u, CTR3: %u",
+				__entry->old_pid, __entry->new_pid,
+				__entry->cctr, __entry->ctr0, __entry->ctr1,
+				__entry->ctr2, __entry->ctr3)
+);
+
+#endif
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE perf_trace_counters
+#include <trace/define_trace.h>
+