ARM: Perfevents: Add mode exclusion support for Krait P2

Mode exclusion allows for counting filtered by the mode
the CPU in running in.

e.g.

perf stat -e rXXX:u to count only userspace activity

perf stat -e rXXX:k to count only kernel activity

Change-Id: I6cf6035d3cfe8d3ee8534ffe130eac6e965aa899
Signed-off-by: Ashwin Chaugule <ashwinc@codeaurora.org>
diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c
index be324ac..9144461 100644
--- a/arch/arm/kernel/perf_event.c
+++ b/arch/arm/kernel/perf_event.c
@@ -82,6 +82,8 @@
 					 struct hw_perf_event *hwc);
 	u32		(*read_counter)(int idx);
 	void		(*write_counter)(int idx, u32 val);
+	int             (*set_event_filter) (struct hw_perf_event *evt,
+			struct perf_event_attr *attr);
 	void		(*start)(void);
 	void		(*stop)(void);
 	void		(*reset)(void *);
@@ -472,6 +474,13 @@
 	pmu_device = NULL;
 }
 
+static int
+event_requires_mode_exclusion(struct perf_event_attr *attr)
+{
+	return attr->exclude_idle || attr->exclude_user ||
+		attr->exclude_kernel || attr->exclude_hv;
+}
+
 static atomic_t active_events = ATOMIC_INIT(0);
 static DEFINE_MUTEX(pmu_reserve_mutex);
 
@@ -508,17 +517,6 @@
 		return mapping;
 	}
 
-	/*
-	 * Check whether we need to exclude the counter from certain modes.
-	 * The ARM performance counters are on all of the time so if someone
-	 * has asked us for some excludes then we have to fail.
-	 */
-	if (event->attr.exclude_kernel || event->attr.exclude_user ||
-	    event->attr.exclude_hv || event->attr.exclude_idle) {
-		pr_debug("ARM performance counters do not support "
-			 "mode exclusion\n");
-		return -EPERM;
-	}
 
 	/*
 	 * We don't assign an index until we actually place the event onto
@@ -534,13 +532,23 @@
 	 * the event mapping and the counter to use. The counter to use is
 	 * also the indx and the config_base is the event type.
 	 */
-	hwc->config_base	    = (unsigned long)mapping;
-	hwc->config		    = 0;
-	hwc->event_base		    = 0;
+	hwc->config_base = 0;
+	hwc->config = 0;
+	hwc->event_base = 0;
+
+	if ((!armpmu->set_event_filter ||
+		armpmu->set_event_filter(hwc, &event->attr)) &&
+		event_requires_mode_exclusion(&event->attr)) {
+		pr_debug("ARM performance counters do not support "
+				"mode exclusion\n");
+		return -EPERM;
+	}
+
+	hwc->config_base |= (unsigned long)mapping;
 
 	if (!hwc->sample_period) {
 		hwc->sample_period  = armpmu->max_period;
-		hwc->last_period    = hwc->sample_period;
+		hwc->last_period = hwc->sample_period;
 		local64_set(&hwc->period_left, hwc->sample_period);
 	}