Merge commit 'AU_LINUX_ANDROID_ICS.04.00.04.00.126' into msm-3.4

AU_LINUX_ANDROID_ICS.04.00.04.00.126 from msm-3.0.
First parent is from google/android-3.4.

* commit 'AU_LINUX_ANDROID_ICS.04.00.04.00.126': (8712 commits)
  PRNG: Device tree entry for qrng device.
  vidc:1080p: Set video core timeout value for Thumbnail mode
  msm: sps: improve the debugging support in SPS driver
  board-8064 msm: Overlap secure and non secure video firmware heaps.
  msm: clock: Add handoff ops for 7x30 and copper XO clocks
  msm_fb: display: Wait for external vsync before DTV IOMMU unmap
  msm: Fix ciruclar dependency in debug UART settings
  msm: gdsc: Add GDSC regulator driver for msm-copper
  defconfig: Enable Mobicore Driver.
  mobicore: Add mobicore driver.
  mobicore: rename variable to lower case.
  mobicore: rename folder.
  mobicore: add makefiles
  mobicore: initial import of kernel driver
  ASoC: msm: Add SLIMBUS_2_RX CPU DAI
  board-8064-gpio: Update FUNC for EPM SPI CS
  msm_fb: display: Remove chicken bit config during video playback
  mmc: msm_sdcc: enable the sanitize capability
  msm-fb: display: lm2 writeback support on mpq platfroms
  msm_fb: display: Disable LVDS phy & pll during panel off
  ...

Signed-off-by: Steve Muckle <smuckle@codeaurora.org>
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index 7b787d6..22b0f1e 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -34,6 +34,7 @@
 obj-$(CONFIG_SMP)		+= smp.o smp_tlb.o
 obj-$(CONFIG_HAVE_ARM_SCU)	+= smp_scu.o
 obj-$(CONFIG_HAVE_ARM_TWD)	+= smp_twd.o
+obj-$(CONFIG_ARM_ARCH_TIMER)	+= arch_timer.o
 obj-$(CONFIG_DYNAMIC_FTRACE)	+= ftrace.o insn.o
 obj-$(CONFIG_FUNCTION_GRAPH_TRACER)	+= ftrace.o insn.o
 obj-$(CONFIG_JUMP_LABEL)	+= jump_label.o insn.o patch.o
diff --git a/arch/arm/kernel/arch_timer.c b/arch/arm/kernel/arch_timer.c
new file mode 100644
index 0000000..81a9a71
--- /dev/null
+++ b/arch/arm/kernel/arch_timer.c
@@ -0,0 +1,378 @@
+/*
+ *  linux/arch/arm/kernel/arch_timer.c
+ *
+ *  Copyright (C) 2011 ARM Ltd.
+ *  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 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timex.h>
+#include <linux/device.h>
+#include <linux/smp.h>
+#include <linux/cpu.h>
+#include <linux/jiffies.h>
+#include <linux/clockchips.h>
+#include <linux/interrupt.h>
+#include <linux/of_irq.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+
+#include <asm/cputype.h>
+#include <asm/localtimer.h>
+#include <asm/arch_timer.h>
+#include <asm/sched_clock.h>
+#include <asm/hardware/gic.h>
+#include <asm/system_info.h>
+
+static unsigned long arch_timer_rate;
+static int arch_timer_ppi;
+static int arch_timer_ppi2;
+
+static struct clock_event_device __percpu **arch_timer_evt;
+
+/*
+ * Architected system timer support.
+ */
+
+#define ARCH_TIMER_CTRL_ENABLE		(1 << 0)
+#define ARCH_TIMER_CTRL_IT_MASK		(1 << 1)
+#define ARCH_TIMER_CTRL_IT_STAT		(1 << 2)
+
+#define ARCH_TIMER_REG_CTRL		0
+#define ARCH_TIMER_REG_FREQ		1
+#define ARCH_TIMER_REG_TVAL		2
+
+static void arch_timer_reg_write(int reg, u32 val)
+{
+	switch (reg) {
+	case ARCH_TIMER_REG_CTRL:
+		asm volatile("mcr p15, 0, %0, c14, c2, 1" : : "r" (val));
+		break;
+	case ARCH_TIMER_REG_TVAL:
+		asm volatile("mcr p15, 0, %0, c14, c2, 0" : : "r" (val));
+		break;
+	}
+
+	isb();
+}
+
+static u32 arch_timer_reg_read(int reg)
+{
+	u32 val;
+
+	switch (reg) {
+	case ARCH_TIMER_REG_CTRL:
+		asm volatile("mrc p15, 0, %0, c14, c2, 1" : "=r" (val));
+		break;
+	case ARCH_TIMER_REG_FREQ:
+		asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (val));
+		break;
+	case ARCH_TIMER_REG_TVAL:
+		asm volatile("mrc p15, 0, %0, c14, c2, 0" : "=r" (val));
+		break;
+	default:
+		BUG();
+	}
+
+	return val;
+}
+
+static irqreturn_t arch_timer_handler(int irq, void *dev_id)
+{
+	struct clock_event_device *evt;
+	unsigned long ctrl;
+
+	ctrl = arch_timer_reg_read(ARCH_TIMER_REG_CTRL);
+	if (ctrl & ARCH_TIMER_CTRL_IT_STAT) {
+		ctrl |= ARCH_TIMER_CTRL_IT_MASK;
+		arch_timer_reg_write(ARCH_TIMER_REG_CTRL, ctrl);
+		evt = *__this_cpu_ptr(arch_timer_evt);
+		evt->event_handler(evt);
+		return IRQ_HANDLED;
+	}
+
+	return IRQ_NONE;
+}
+
+static void arch_timer_disable(void)
+{
+	unsigned long ctrl;
+
+	ctrl = arch_timer_reg_read(ARCH_TIMER_REG_CTRL);
+	ctrl &= ~ARCH_TIMER_CTRL_ENABLE;
+	arch_timer_reg_write(ARCH_TIMER_REG_CTRL, ctrl);
+}
+
+static void arch_timer_set_mode(enum clock_event_mode mode,
+				struct clock_event_device *clk)
+{
+	switch (mode) {
+	case CLOCK_EVT_MODE_UNUSED:
+	case CLOCK_EVT_MODE_SHUTDOWN:
+		arch_timer_disable();
+		break;
+	default:
+		break;
+	}
+}
+
+static int arch_timer_set_next_event(unsigned long evt,
+				     struct clock_event_device *unused)
+{
+	unsigned long ctrl;
+
+	ctrl = arch_timer_reg_read(ARCH_TIMER_REG_CTRL);
+	ctrl |= ARCH_TIMER_CTRL_ENABLE;
+	ctrl &= ~ARCH_TIMER_CTRL_IT_MASK;
+
+	arch_timer_reg_write(ARCH_TIMER_REG_CTRL, ctrl);
+	arch_timer_reg_write(ARCH_TIMER_REG_TVAL, evt);
+
+	return 0;
+}
+
+static int __cpuinit arch_timer_setup(struct clock_event_device *clk)
+{
+	/* setup clock event only once for CPU 0 */
+	if (!smp_processor_id() && clk->irq == arch_timer_ppi)
+		return 0;
+
+	/* Be safe... */
+	arch_timer_disable();
+
+	clk->features = CLOCK_EVT_FEAT_ONESHOT;
+	clk->name = "arch_sys_timer";
+	clk->rating = 450;
+	clk->set_mode = arch_timer_set_mode;
+	clk->set_next_event = arch_timer_set_next_event;
+	clk->irq = arch_timer_ppi;
+
+	clockevents_config_and_register(clk, arch_timer_rate,
+					0xf, 0x7fffffff);
+
+	*__this_cpu_ptr(arch_timer_evt) = clk;
+
+	enable_percpu_irq(clk->irq, 0);
+	if (arch_timer_ppi2)
+		enable_percpu_irq(arch_timer_ppi2, 0);
+
+	return 0;
+}
+
+/* Is the optional system timer available? */
+static int local_timer_is_architected(void)
+{
+	return (cpu_architecture() >= CPU_ARCH_ARMv7) &&
+	       ((read_cpuid_ext(CPUID_EXT_PFR1) >> 16) & 0xf) == 1;
+}
+
+static int arch_timer_available(void)
+{
+	unsigned long freq;
+
+	if (!local_timer_is_architected())
+		return -ENXIO;
+
+	if (arch_timer_rate == 0) {
+		arch_timer_reg_write(ARCH_TIMER_REG_CTRL, 0);
+		freq = arch_timer_reg_read(ARCH_TIMER_REG_FREQ);
+
+		/* Check the timer frequency. */
+		if (freq == 0) {
+			pr_warn("Architected timer frequency not available\n");
+			return -EINVAL;
+		}
+
+		arch_timer_rate = freq;
+		pr_info("Architected local timer running at %lu.%02luMHz.\n",
+			freq / 1000000, (freq / 10000) % 100);
+	}
+
+	return 0;
+}
+
+static inline cycle_t arch_counter_get_cntpct(void)
+{
+	u32 cvall, cvalh;
+
+	asm volatile("mrrc p15, 0, %0, %1, c14" : "=r" (cvall), "=r" (cvalh));
+
+	return ((cycle_t) cvalh << 32) | cvall;
+}
+
+static inline cycle_t arch_counter_get_cntvct(void)
+{
+	u32 cvall, cvalh;
+
+	asm volatile("mrrc p15, 1, %0, %1, c14" : "=r" (cvall), "=r" (cvalh));
+
+	return ((cycle_t) cvalh << 32) | cvall;
+}
+
+static cycle_t arch_counter_read(struct clocksource *cs)
+{
+	return arch_counter_get_cntpct();
+}
+
+#ifdef ARCH_HAS_READ_CURRENT_TIMER
+int read_current_timer(unsigned long *timer_val)
+{
+	*timer_val = (unsigned long)arch_counter_get_cntpct();
+	return 0;
+}
+#endif
+
+static struct clocksource clocksource_counter = {
+	.name	= "arch_sys_counter",
+	.rating	= 400,
+	.read	= arch_counter_read,
+	.mask	= CLOCKSOURCE_MASK(56),
+	.flags	= CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+static u32 arch_counter_get_cntvct32(void)
+{
+	cycle_t cntvct;
+
+	cntvct = arch_counter_get_cntvct();
+
+	/*
+	 * The sched_clock infrastructure only knows about counters
+	 * with at most 32bits. Forget about the upper 24 bits for the
+	 * time being...
+	 */
+	return (u32)(cntvct & (u32)~0);
+}
+
+static u32 notrace arch_timer_update_sched_clock(void)
+{
+	return arch_counter_get_cntvct32();
+}
+
+static void __cpuinit arch_timer_stop(struct clock_event_device *clk)
+{
+	pr_debug("arch_timer_teardown disable IRQ%d cpu #%d\n",
+		 clk->irq, smp_processor_id());
+	disable_percpu_irq(clk->irq);
+	if (arch_timer_ppi2)
+		disable_percpu_irq(arch_timer_ppi2);
+	arch_timer_set_mode(CLOCK_EVT_MODE_UNUSED, clk);
+}
+
+static struct local_timer_ops arch_timer_ops __cpuinitdata = {
+	.setup	= arch_timer_setup,
+	.stop	= arch_timer_stop,
+};
+
+static int __init arch_timer_common_register(void)
+{
+	int err;
+
+	err = arch_timer_available();
+	if (err)
+		return err;
+
+	arch_timer_evt = alloc_percpu(struct clock_event_device *);
+	if (!arch_timer_evt)
+		return -ENOMEM;
+
+	clocksource_register_hz(&clocksource_counter, arch_timer_rate);
+
+	setup_sched_clock(arch_timer_update_sched_clock, 32, arch_timer_rate);
+
+#ifdef ARCH_HAS_READ_CURRENT_TIMER
+	set_delay_fn(read_current_timer_delay_loop);
+#endif
+
+	err = request_percpu_irq(arch_timer_ppi, arch_timer_handler,
+				 "arch_timer", arch_timer_evt);
+	if (err) {
+		pr_err("arch_timer: can't register interrupt %d (%d)\n",
+		       arch_timer_ppi, err);
+		goto out_free;
+	}
+
+	if (arch_timer_ppi2) {
+		err = request_percpu_irq(arch_timer_ppi2, arch_timer_handler,
+					 "arch_timer", arch_timer_evt);
+		if (err) {
+			pr_err("arch_timer: can't register interrupt %d (%d)\n",
+			       arch_timer_ppi2, err);
+			arch_timer_ppi2 = 0;
+			goto out_free_irq;
+		}
+	}
+
+	err = local_timer_register(&arch_timer_ops);
+	if (err)
+		goto out_free_irq;
+	percpu_timer_setup();
+
+	return 0;
+
+out_free_irq:
+	free_percpu_irq(arch_timer_ppi, arch_timer_evt);
+	if (arch_timer_ppi2)
+		free_percpu_irq(arch_timer_ppi2, arch_timer_evt);
+
+out_free:
+	free_percpu(arch_timer_evt);
+
+	return err;
+}
+
+int __init arch_timer_register(struct arch_timer *at)
+{
+	if (at->res[0].start <= 0 || !(at->res[0].flags & IORESOURCE_IRQ))
+		return -EINVAL;
+
+	arch_timer_ppi = at->res[0].start;
+
+	if (at->res[1].start > 0 && (at->res[1].flags & IORESOURCE_IRQ))
+		arch_timer_ppi2 = at->res[1].start;
+
+	return arch_timer_common_register();
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id arch_timer_of_match[] __initconst = {
+	{ .compatible	= "arm,armv7-timer",	},
+	{},
+};
+
+int __init arch_timer_of_register(void)
+{
+	struct device_node *np;
+	u32 freq;
+	int ret;
+
+	np = of_find_matching_node(NULL, arch_timer_of_match);
+	if (!np) {
+		pr_err("arch_timer: can't find DT node\n");
+		return -ENODEV;
+	}
+
+	/* Try to determine the frequency from the device tree or CNTFRQ */
+	if (!of_property_read_u32(np, "clock-frequency", &freq))
+		arch_timer_rate = freq;
+
+	ret = irq_of_parse_and_map(np, 0);
+	if (ret <= 0) {
+		pr_err("arch_timer: interrupt not specified in timer node\n");
+		return -ENODEV;
+	}
+	arch_timer_ppi = ret;
+	ret = irq_of_parse_and_map(np, 1);
+	if (ret > 0)
+		arch_timer_ppi2 = ret;
+	pr_info("arch_timer: found %s irqs %d %d\n",
+		np->name, arch_timer_ppi, arch_timer_ppi2);
+
+	return arch_timer_common_register();
+}
+#endif
diff --git a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c
index b57c75e..f1a50f3 100644
--- a/arch/arm/kernel/armksyms.c
+++ b/arch/arm/kernel/armksyms.c
@@ -48,10 +48,6 @@
 
 extern void fpundefinstr(void);
 
-	/* platform dependent support */
-EXPORT_SYMBOL(__udelay);
-EXPORT_SYMBOL(__const_udelay);
-
 	/* networking */
 EXPORT_SYMBOL(csum_partial);
 EXPORT_SYMBOL(csum_partial_copy_from_user);
diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S
index 7fd3ad0..7c44acd 100644
--- a/arch/arm/kernel/entry-armv.S
+++ b/arch/arm/kernel/entry-armv.S
@@ -713,8 +713,15 @@
 	ldr	r7, [r7, #TSK_STACK_CANARY]
 #endif
 #ifdef CONFIG_CPU_USE_DOMAINS
+#ifdef CONFIG_EMULATE_DOMAIN_MANAGER_V7
+	stmdb	r13!, {r0-r3, lr}
+	mov	r0, r6
+	bl	emulate_domain_manager_set
+	ldmia	r13!, {r0-r3, lr}
+#else
 	mcr	p15, 0, r6, c3, c0, 0		@ Set domain register
 #endif
+#endif
 	mov	r5, r0
 	add	r4, r2, #TI_CPU_SAVE
 	ldr	r0, =thread_notify_head
diff --git a/arch/arm/kernel/ftrace.c b/arch/arm/kernel/ftrace.c
index bf17145..1ef5022 100644
--- a/arch/arm/kernel/ftrace.c
+++ b/arch/arm/kernel/ftrace.c
@@ -78,6 +78,20 @@
 	return 0;
 }
 
+int ftrace_arch_code_modify_prepare(void)
+{
+	set_kernel_text_rw();
+	set_all_modules_text_rw();
+	return 0;
+}
+
+int ftrace_arch_code_modify_post_process(void)
+{
+	set_all_modules_text_ro();
+	set_kernel_text_ro();
+	return 0;
+}
+
 static unsigned long ftrace_call_replace(unsigned long pc, unsigned long addr)
 {
 	return arm_gen_branch_link(pc, addr);
diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S
index 3bf0c7f..313ff0b 100644
--- a/arch/arm/kernel/head.S
+++ b/arch/arm/kernel/head.S
@@ -337,7 +337,6 @@
 	.long	__turn_mmu_on_end
 
 #if defined(CONFIG_SMP)
-	__CPUINIT
 ENTRY(secondary_startup)
 	/*
 	 * Common entry point for secondary CPUs.
@@ -422,10 +421,17 @@
 	mov	r5, #0
 	mcrr	p15, 0, r4, r5, c2		@ load TTBR0
 #else
-	mov	r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \
-		      domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \
-		      domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \
+#ifdef CONFIG_EMULATE_DOMAIN_MANAGER_V7
+	mov	r5, #(domain_val(DOMAIN_USER, DOMAIN_CLIENT) | \
+		      domain_val(DOMAIN_KERNEL, DOMAIN_CLIENT) | \
+		      domain_val(DOMAIN_TABLE, DOMAIN_CLIENT) | \
 		      domain_val(DOMAIN_IO, DOMAIN_CLIENT))
+#else
+	mov	r5, #(domain_val(DOMAIN_USER, DOMAIN_CLIENT) | \
+		      domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \
+		      domain_val(DOMAIN_TABLE, DOMAIN_CLIENT) | \
+		      domain_val(DOMAIN_IO, DOMAIN_CLIENT))
+#endif
 	mcr	p15, 0, r5, c3, c0, 0		@ load domain access register
 	mcr	p15, 0, r4, c2, c0, 0		@ load page table pointer
 #endif
@@ -455,6 +461,15 @@
 	mrc	p15, 0, r3, c0, c0, 0		@ read id reg
 	instr_sync
 	mov	r3, r3
+#ifdef CONFIG_ARCH_MSM_KRAIT
+	ldr	r3, =0xff00fc00
+	and	r3, r9, r3
+	ldr 	r4, =0x51000400
+	cmp	r3, r4
+	mrceq	p15, 7, r3, c15, c0, 2
+	biceq	r3, r3, #0x400
+	mcreq	p15, 7, r3, c15, c0, 2
+#endif
 	mov	r3, r13
 	mov	pc, r3
 __turn_mmu_on_end:
diff --git a/arch/arm/kernel/hw_breakpoint.c b/arch/arm/kernel/hw_breakpoint.c
index ba386bd..ff2c0ad 100644
--- a/arch/arm/kernel/hw_breakpoint.c
+++ b/arch/arm/kernel/hw_breakpoint.c
@@ -853,6 +853,18 @@
 	return ret;
 }
 
+static void reset_brps_reserved_reg(int n)
+{
+	int i;
+
+	/* we must also reset any reserved registers. */
+	for (i = 0; i < n; ++i) {
+		write_wb_reg(ARM_BASE_BCR + i, 0UL);
+		write_wb_reg(ARM_BASE_BVR + i, 0UL);
+	}
+
+}
+
 /*
  * One-time initialisation.
  */
@@ -938,12 +950,11 @@
 	if (enable_monitor_mode())
 		return;
 
-	/* We must also reset any reserved registers. */
-	raw_num_brps = get_num_brp_resources();
-	for (i = 0; i < raw_num_brps; ++i) {
-		write_wb_reg(ARM_BASE_BCR + i, 0UL);
-		write_wb_reg(ARM_BASE_BVR + i, 0UL);
-	}
+#ifdef CONFIG_HAVE_HW_BRKPT_RESERVED_RW_ACCESS
+	reset_brps_reserved_reg(core_num_brps);
+#else
+	reset_brps_reserved_reg(core_num_brps + core_num_reserved_brps);
+#endif
 
 	for (i = 0; i < core_num_wrps; ++i) {
 		write_wb_reg(ARM_BASE_WCR + i, 0UL);
diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c
index 8349d4e..bc48bff 100644
--- a/arch/arm/kernel/irq.c
+++ b/arch/arm/kernel/irq.c
@@ -40,6 +40,8 @@
 #include <asm/mach/irq.h>
 #include <asm/mach/time.h>
 
+#include <asm/perftypes.h>
+
 /*
  * No architecture-specific irq_finish function defined in arm/arch/irqs.h.
  */
@@ -71,6 +73,7 @@
 {
 	struct pt_regs *old_regs = set_irq_regs(regs);
 
+	perf_mon_interrupt_in();
 	irq_enter();
 
 	/*
@@ -90,6 +93,7 @@
 
 	irq_exit();
 	set_irq_regs(old_regs);
+	perf_mon_interrupt_out();
 }
 
 /*
@@ -128,8 +132,18 @@
 #ifdef CONFIG_SPARSE_IRQ
 int __init arch_probe_nr_irqs(void)
 {
-	nr_irqs = machine_desc->nr_irqs ? machine_desc->nr_irqs : NR_IRQS;
-	return nr_irqs;
+	/*
+	 * machine_desc->nr_irqs < 0 is a special case that
+	 * specifies not to preallocate any irq_descs.
+	 */
+	if (machine_desc->nr_irqs < 0) {
+		nr_irqs = 0;
+		return nr_irqs;
+	} else {
+		nr_irqs = machine_desc->nr_irqs ?
+			  machine_desc->nr_irqs : NR_IRQS;
+		return nr_irqs;
+	}
 }
 #endif
 
diff --git a/arch/arm/kernel/patch.c b/arch/arm/kernel/patch.c
index 07314af..7a27c47 100644
--- a/arch/arm/kernel/patch.c
+++ b/arch/arm/kernel/patch.c
@@ -5,6 +5,7 @@
 #include <asm/cacheflush.h>
 #include <asm/smp_plat.h>
 #include <asm/opcodes.h>
+#include <asm/mmu_writeable.h>
 
 #include "patch.h"
 
@@ -17,6 +18,10 @@
 {
 	bool thumb2 = IS_ENABLED(CONFIG_THUMB2_KERNEL);
 	int size;
+	unsigned long flags;
+
+	mem_text_writeable_spinlock(&flags);
+	mem_text_address_writeable((unsigned long)addr);
 
 	if (thumb2 && __opcode_is_thumb16(insn)) {
 		*(u16 *)addr = __opcode_to_mem_thumb16(insn);
@@ -42,6 +47,9 @@
 
 	flush_icache_range((uintptr_t)(addr),
 			   (uintptr_t)(addr) + size);
+
+	mem_text_address_restore();
+	mem_text_writeable_spinunlock(&flags);
 }
 
 static int __kprobes patch_text_stop_machine(void *data)
diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c
index 186c8cb..af6e7e6 100644
--- a/arch/arm/kernel/perf_event.c
+++ b/arch/arm/kernel/perf_event.c
@@ -20,6 +20,7 @@
 #include <linux/platform_device.h>
 #include <linux/spinlock.h>
 #include <linux/uaccess.h>
+#include <linux/irq.h>
 
 #include <asm/cputype.h>
 #include <asm/irq.h>
@@ -78,7 +79,7 @@
 #define CACHE_OP_UNSUPPORTED		0xFFFF
 
 static int
-armpmu_map_cache_event(const unsigned (*cache_map)
+armpmu_map_cache_event(unsigned (*cache_map)
 				      [PERF_COUNT_HW_CACHE_MAX]
 				      [PERF_COUNT_HW_CACHE_OP_MAX]
 				      [PERF_COUNT_HW_CACHE_RESULT_MAX],
@@ -121,7 +122,7 @@
 
 static int map_cpu_event(struct perf_event *event,
 			 const unsigned (*event_map)[PERF_COUNT_HW_MAX],
-			 const unsigned (*cache_map)
+			 unsigned (*cache_map)
 					[PERF_COUNT_HW_CACHE_MAX]
 					[PERF_COUNT_HW_CACHE_OP_MAX]
 					[PERF_COUNT_HW_CACHE_RESULT_MAX],
@@ -253,7 +254,7 @@
 	 * happened since disabling.
 	 */
 	armpmu_event_set_period(event, hwc, hwc->idx);
-	armpmu->enable(hwc, hwc->idx);
+	armpmu->enable(hwc, hwc->idx, event->cpu);
 }
 
 static void
@@ -362,25 +363,44 @@
 	return plat->handle_irq(irq, dev, armpmu->handle_irq);
 }
 
+int
+armpmu_generic_request_irq(int irq, irq_handler_t *handle_irq)
+{
+        return request_irq(irq, *handle_irq,
+                        IRQF_DISABLED | IRQF_NOBALANCING,
+                        "armpmu", NULL);
+}
+
+void
+armpmu_generic_free_irq(int irq)
+{
+        if (irq >= 0)
+                free_irq(irq, NULL);
+}
+
 static void
 armpmu_release_hardware(struct arm_pmu *armpmu)
 {
 	int i, irq, irqs;
 	struct platform_device *pmu_device = armpmu->plat_device;
+#if 0
 	struct arm_pmu_platdata *plat =
 		dev_get_platdata(&pmu_device->dev);
-
+#endif
 	irqs = min(pmu_device->num_resources, num_possible_cpus());
 
 	for (i = 0; i < irqs; ++i) {
 		if (!cpumask_test_and_clear_cpu(i, &armpmu->active_irqs))
 			continue;
 		irq = platform_get_irq(pmu_device, i);
+		armpmu->free_pmu_irq(irq);
+#if 0
 		if (irq >= 0) {
 			if (plat && plat->disable_irq)
 				plat->disable_irq(irq);
 			free_irq(irq, armpmu);
 		}
+#endif
 	}
 
 	release_pmu(armpmu->type);
@@ -432,6 +452,17 @@
 			continue;
 		}
 
+		err = armpmu->request_pmu_irq(irq, &handle_irq);
+
+                if (err) {
+                        pr_warning("unable to request IRQ%d for %s perf "
+                                "counters\n", irq, armpmu->name);
+
+			armpmu_release_hardware(cpu_pmu);
+                        return err;
+                }
+
+#if 0
 		err = request_irq(irq, handle_irq,
 				  IRQF_DISABLED | IRQF_NOBALANCING,
 				  "arm-pmu", armpmu);
@@ -442,6 +473,7 @@
 			return err;
 		} else if (plat && plat->enable_irq)
 			plat->enable_irq(irq);
+#endif
 
 		cpumask_set_cpu(i, &armpmu->active_irqs);
 	}
@@ -611,6 +643,7 @@
 #include "perf_event_xscale.c"
 #include "perf_event_v6.c"
 #include "perf_event_v7.c"
+#include "perf_event_msm_krait.c"
 
 /*
  * Ensure the PMU has sane values out of reset.
@@ -637,7 +670,7 @@
 };
 
 static struct platform_device_id armpmu_plat_device_ids[] = {
-	{.name = "arm-pmu"},
+	{.name = "cpu-arm-pmu"},
 	{},
 };
 
@@ -652,7 +685,7 @@
 
 static struct platform_driver armpmu_driver = {
 	.driver		= {
-		.name	= "arm-pmu",
+		.name	= "cpu-arm-pmu",
 		.of_match_table = armpmu_of_device_ids,
 	},
 	.probe		= armpmu_device_probe,
@@ -753,8 +786,28 @@
 			cpu_pmu = xscale2pmu_init();
 			break;
 		}
+	/* Qualcomm CPUs */
+	} else if (0x51 == implementor) {
+		switch (part_number) {
+		case 0x00F0:    /* 8x50 & 7x30*/
+//			cpu_pmu = armv7_scorpion_pmu_init();
+			break;
+		case 0x02D0:    /* 8x60 */
+//			fabricmon_pmu_init();
+//			cpu_pmu = armv7_scorpionmp_pmu_init();
+//			scorpionmp_l2_pmu_init();
+			break;
+		case 0x0490:    /* 8960 sim */
+		case 0x04D0:    /* 8960 */
+		case 0x06F0:    /* 8064 */
+//			fabricmon_pmu_init();
+			cpu_pmu = armv7_krait_pmu_init();
+//			krait_l2_pmu_init();
+			break;
+		}
 	}
 
+
 	if (cpu_pmu) {
 		pr_info("enabled with %s PMU driver, %d counters available\n",
 			cpu_pmu->name, cpu_pmu->num_events);
diff --git a/arch/arm/kernel/perf_event_msm.c b/arch/arm/kernel/perf_event_msm.c
new file mode 100644
index 0000000..0ca5164
--- /dev/null
+++ b/arch/arm/kernel/perf_event_msm.c
@@ -0,0 +1,792 @@
+/*
+ * Copyright (c) 2011-2012, Code Aurora Forum. 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/cpumask.h>
+
+#include <asm/vfp.h>
+#include <asm/system.h>
+#include "../vfp/vfpinstr.h"
+
+#ifdef CONFIG_CPU_V7
+#define SCORPION_EVT_PREFIX 1
+#define SCORPION_MAX_L1_REG 4
+
+#define SCORPION_EVTYPE_EVENT 0xfffff
+
+static u32 scorpion_evt_type_base[] = {0x4c, 0x50, 0x54, 0x58, 0x5c};
+
+enum scorpion_perf_common {
+	SCORPION_EVT_START_IDX			= 0x4c,
+	SCORPION_ICACHE_EXPL_INV		= 0x4c,
+	SCORPION_ICACHE_MISS			= 0x4d,
+	SCORPION_ICACHE_ACCESS			= 0x4e,
+	SCORPION_ICACHE_CACHEREQ_L2		= 0x4f,
+	SCORPION_ICACHE_NOCACHE_L2		= 0x50,
+	SCORPION_HIQUP_NOPED			= 0x51,
+	SCORPION_DATA_ABORT			= 0x52,
+	SCORPION_IRQ				= 0x53,
+	SCORPION_FIQ				= 0x54,
+	SCORPION_ALL_EXCPT			= 0x55,
+	SCORPION_UNDEF				= 0x56,
+	SCORPION_SVC				= 0x57,
+	SCORPION_SMC				= 0x58,
+	SCORPION_PREFETCH_ABORT			= 0x59,
+	SCORPION_INDEX_CHECK			= 0x5a,
+	SCORPION_NULL_CHECK			= 0x5b,
+	SCORPION_ICIMVAU_IMPL_ICIALLU		= 0x5c,
+	SCORPION_NONICIALLU_BTAC_INV		= 0x5d,
+	SCORPION_IMPL_ICIALLU			= 0x5e,
+	SCORPION_EXPL_ICIALLU			= 0x5f,
+	SCORPION_SPIPE_ONLY_CYCLES		= 0x60,
+	SCORPION_XPIPE_ONLY_CYCLES		= 0x61,
+	SCORPION_DUAL_CYCLES			= 0x62,
+	SCORPION_DISPATCH_ANY_CYCLES		= 0x63,
+	SCORPION_FIFO_FULLBLK_CMT		= 0x64,
+	SCORPION_FAIL_COND_INST			= 0x65,
+	SCORPION_PASS_COND_INST			= 0x66,
+	SCORPION_ALLOW_VU_CLK			= 0x67,
+	SCORPION_VU_IDLE			= 0x68,
+	SCORPION_ALLOW_L2_CLK			= 0x69,
+	SCORPION_L2_IDLE			= 0x6a,
+	SCORPION_DTLB_IMPL_INV_SCTLR_DACR	= 0x6b,
+	SCORPION_DTLB_EXPL_INV			= 0x6c,
+	SCORPION_DTLB_MISS			= 0x6d,
+	SCORPION_DTLB_ACCESS			= 0x6e,
+	SCORPION_ITLB_MISS			= 0x6f,
+	SCORPION_ITLB_IMPL_INV			= 0x70,
+	SCORPION_ITLB_EXPL_INV			= 0x71,
+	SCORPION_UTLB_D_MISS			= 0x72,
+	SCORPION_UTLB_D_ACCESS			= 0x73,
+	SCORPION_UTLB_I_MISS			= 0x74,
+	SCORPION_UTLB_I_ACCESS			= 0x75,
+	SCORPION_UTLB_INV_ASID			= 0x76,
+	SCORPION_UTLB_INV_MVA			= 0x77,
+	SCORPION_UTLB_INV_ALL			= 0x78,
+	SCORPION_S2_HOLD_RDQ_UNAVAIL		= 0x79,
+	SCORPION_S2_HOLD			= 0x7a,
+	SCORPION_S2_HOLD_DEV_OP			= 0x7b,
+	SCORPION_S2_HOLD_ORDER			= 0x7c,
+	SCORPION_S2_HOLD_BARRIER		= 0x7d,
+	SCORPION_VIU_DUAL_CYCLE			= 0x7e,
+	SCORPION_VIU_SINGLE_CYCLE		= 0x7f,
+	SCORPION_VX_PIPE_WAR_STALL_CYCLES	= 0x80,
+	SCORPION_VX_PIPE_WAW_STALL_CYCLES	= 0x81,
+	SCORPION_VX_PIPE_RAW_STALL_CYCLES	= 0x82,
+	SCORPION_VX_PIPE_LOAD_USE_STALL		= 0x83,
+	SCORPION_VS_PIPE_WAR_STALL_CYCLES	= 0x84,
+	SCORPION_VS_PIPE_WAW_STALL_CYCLES	= 0x85,
+	SCORPION_VS_PIPE_RAW_STALL_CYCLES	= 0x86,
+	SCORPION_EXCEPTIONS_INV_OPERATION	= 0x87,
+	SCORPION_EXCEPTIONS_DIV_BY_ZERO		= 0x88,
+	SCORPION_COND_INST_FAIL_VX_PIPE		= 0x89,
+	SCORPION_COND_INST_FAIL_VS_PIPE		= 0x8a,
+	SCORPION_EXCEPTIONS_OVERFLOW		= 0x8b,
+	SCORPION_EXCEPTIONS_UNDERFLOW		= 0x8c,
+	SCORPION_EXCEPTIONS_DENORM		= 0x8d,
+};
+
+enum scorpion_perf_smp {
+	SCORPIONMP_NUM_BARRIERS			= 0x8e,
+	SCORPIONMP_BARRIER_CYCLES		= 0x8f,
+};
+
+enum scorpion_perf_up {
+	SCORPION_BANK_AB_HIT			= 0x8e,
+	SCORPION_BANK_AB_ACCESS			= 0x8f,
+	SCORPION_BANK_CD_HIT			= 0x90,
+	SCORPION_BANK_CD_ACCESS			= 0x91,
+	SCORPION_BANK_AB_DSIDE_HIT		= 0x92,
+	SCORPION_BANK_AB_DSIDE_ACCESS		= 0x93,
+	SCORPION_BANK_CD_DSIDE_HIT		= 0x94,
+	SCORPION_BANK_CD_DSIDE_ACCESS		= 0x95,
+	SCORPION_BANK_AB_ISIDE_HIT		= 0x96,
+	SCORPION_BANK_AB_ISIDE_ACCESS		= 0x97,
+	SCORPION_BANK_CD_ISIDE_HIT		= 0x98,
+	SCORPION_BANK_CD_ISIDE_ACCESS		= 0x99,
+	SCORPION_ISIDE_RD_WAIT			= 0x9a,
+	SCORPION_DSIDE_RD_WAIT			= 0x9b,
+	SCORPION_BANK_BYPASS_WRITE		= 0x9c,
+	SCORPION_BANK_AB_NON_CASTOUT		= 0x9d,
+	SCORPION_BANK_AB_L2_CASTOUT		= 0x9e,
+	SCORPION_BANK_CD_NON_CASTOUT		= 0x9f,
+	SCORPION_BANK_CD_L2_CASTOUT		= 0xa0,
+};
+
+static const unsigned armv7_scorpion_perf_map[PERF_COUNT_HW_MAX] = {
+	[PERF_COUNT_HW_CPU_CYCLES]	    = ARMV7_PERFCTR_CPU_CYCLES,
+	[PERF_COUNT_HW_INSTRUCTIONS]	    = ARMV7_PERFCTR_INSTR_EXECUTED,
+	[PERF_COUNT_HW_CACHE_REFERENCES]    = HW_OP_UNSUPPORTED,
+	[PERF_COUNT_HW_CACHE_MISSES]	    = HW_OP_UNSUPPORTED,
+	[PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = ARMV7_PERFCTR_PC_WRITE,
+	[PERF_COUNT_HW_BRANCH_MISSES]	    = ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
+	[PERF_COUNT_HW_BUS_CYCLES]	    = ARMV7_PERFCTR_CLOCK_CYCLES,
+};
+
+static unsigned armv7_scorpion_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
+					  [PERF_COUNT_HW_CACHE_OP_MAX]
+					  [PERF_COUNT_HW_CACHE_RESULT_MAX] = {
+	[C(L1D)] = {
+		/*
+		 * The performance counters don't differentiate between read
+		 * and write accesses/misses so this isn't strictly correct,
+		 * but it's the best we can do. Writes and reads get
+		 * combined.
+		 */
+		[C(OP_READ)] = {
+			[C(RESULT_ACCESS)]	= ARMV7_PERFCTR_DCACHE_ACCESS,
+			[C(RESULT_MISS)]	= ARMV7_PERFCTR_DCACHE_REFILL,
+		},
+		[C(OP_WRITE)] = {
+			[C(RESULT_ACCESS)]	= ARMV7_PERFCTR_DCACHE_ACCESS,
+			[C(RESULT_MISS)]	= ARMV7_PERFCTR_DCACHE_REFILL,
+		},
+		[C(OP_PREFETCH)] = {
+			[C(RESULT_ACCESS)]	= CACHE_OP_UNSUPPORTED,
+			[C(RESULT_MISS)]	= CACHE_OP_UNSUPPORTED,
+		},
+	},
+	[C(L1I)] = {
+		[C(OP_READ)] = {
+			[C(RESULT_ACCESS)]	= SCORPION_ICACHE_ACCESS,
+			[C(RESULT_MISS)]	= SCORPION_ICACHE_MISS,
+		},
+		[C(OP_WRITE)] = {
+			[C(RESULT_ACCESS)]	= SCORPION_ICACHE_ACCESS,
+			[C(RESULT_MISS)]	= SCORPION_ICACHE_MISS,
+		},
+		[C(OP_PREFETCH)] = {
+			[C(RESULT_ACCESS)]	= CACHE_OP_UNSUPPORTED,
+			[C(RESULT_MISS)]	= CACHE_OP_UNSUPPORTED,
+		},
+	},
+	[C(LL)] = {
+		[C(OP_READ)] = {
+			[C(RESULT_ACCESS)]	= CACHE_OP_UNSUPPORTED,
+			[C(RESULT_MISS)]	= CACHE_OP_UNSUPPORTED,
+		},
+		[C(OP_WRITE)] = {
+			[C(RESULT_ACCESS)]	= CACHE_OP_UNSUPPORTED,
+			[C(RESULT_MISS)]	= CACHE_OP_UNSUPPORTED,
+		},
+		[C(OP_PREFETCH)] = {
+			[C(RESULT_ACCESS)]	= CACHE_OP_UNSUPPORTED,
+			[C(RESULT_MISS)]	= CACHE_OP_UNSUPPORTED,
+		},
+	},
+	[C(DTLB)] = {
+		/*
+		 * Only ITLB misses and DTLB refills are supported.
+		 * If users want the DTLB refills misses a raw counter
+		 * must be used.
+		 */
+		[C(OP_READ)] = {
+			[C(RESULT_ACCESS)]	= SCORPION_DTLB_ACCESS,
+			[C(RESULT_MISS)]	= SCORPION_DTLB_MISS,
+		},
+		[C(OP_WRITE)] = {
+			[C(RESULT_ACCESS)]	= SCORPION_DTLB_ACCESS,
+			[C(RESULT_MISS)]	= SCORPION_DTLB_MISS,
+		},
+		[C(OP_PREFETCH)] = {
+			[C(RESULT_ACCESS)]	= CACHE_OP_UNSUPPORTED,
+			[C(RESULT_MISS)]	= CACHE_OP_UNSUPPORTED,
+		},
+	},
+	[C(ITLB)] = {
+		[C(OP_READ)] = {
+			[C(RESULT_ACCESS)]	= CACHE_OP_UNSUPPORTED,
+			[C(RESULT_MISS)]	= SCORPION_ITLB_MISS,
+		},
+		[C(OP_WRITE)] = {
+			[C(RESULT_ACCESS)]	= CACHE_OP_UNSUPPORTED,
+			[C(RESULT_MISS)]	= SCORPION_ITLB_MISS,
+		},
+		[C(OP_PREFETCH)] = {
+			[C(RESULT_ACCESS)]	= CACHE_OP_UNSUPPORTED,
+			[C(RESULT_MISS)]	= CACHE_OP_UNSUPPORTED,
+		},
+	},
+	[C(BPU)] = {
+		[C(OP_READ)] = {
+			[C(RESULT_ACCESS)]
+					= ARMV7_PERFCTR_PC_BRANCH_PRED,
+			[C(RESULT_MISS)]
+					= ARMV7_PERFCTR_PC_BRANCH_PRED,
+		},
+		[C(OP_WRITE)] = {
+			[C(RESULT_ACCESS)]
+					= ARMV7_PERFCTR_PC_BRANCH_PRED,
+			[C(RESULT_MISS)]
+					= ARMV7_PERFCTR_PC_BRANCH_PRED,
+		},
+		[C(OP_PREFETCH)] = {
+			[C(RESULT_ACCESS)]	= CACHE_OP_UNSUPPORTED,
+			[C(RESULT_MISS)]	= CACHE_OP_UNSUPPORTED,
+		},
+	},
+};
+
+struct scorpion_evt {
+	/*
+	 * The scorpion_evt_type field corresponds to the actual Scorpion
+	 * event codes. These map many-to-one to the armv7 defined codes
+	 */
+	u32 scorpion_evt_type;
+
+	/*
+	 * The group_setval field corresponds to the value that the group
+	 * register needs to be set to. This value is deduced from the row
+	 * and column that the event belongs to in the event table
+	 */
+	u32 group_setval;
+
+	/*
+	 * The groupcode corresponds to the group that the event belongs to.
+	 * Scorpion has 5 groups of events LPM0, LPM1, LPM2, L2LPM and VLPM
+	 * going from 0 to 4 in terms of the codes used
+	 */
+	u8 groupcode;
+
+	/*
+	 * The armv7_evt_type field corresponds to the armv7 defined event
+	 * code that the Scorpion events map to
+	 */
+	u32 armv7_evt_type;
+};
+
+static const struct scorpion_evt scorpion_event[] = {
+	{SCORPION_ICACHE_EXPL_INV,		0x80000500, 0, 0x4d},
+	{SCORPION_ICACHE_MISS,			0x80050000, 0, 0x4e},
+	{SCORPION_ICACHE_ACCESS,		0x85000000, 0, 0x4f},
+	{SCORPION_ICACHE_CACHEREQ_L2,		0x86000000, 0, 0x4f},
+	{SCORPION_ICACHE_NOCACHE_L2,		0x87000000, 0, 0x4f},
+	{SCORPION_HIQUP_NOPED,			0x80080000, 0, 0x4e},
+	{SCORPION_DATA_ABORT,			0x8000000a, 0, 0x4c},
+	{SCORPION_IRQ,				0x80000a00, 0, 0x4d},
+	{SCORPION_FIQ,				0x800a0000, 0, 0x4e},
+	{SCORPION_ALL_EXCPT,			0x8a000000, 0, 0x4f},
+	{SCORPION_UNDEF,			0x8000000b, 0, 0x4c},
+	{SCORPION_SVC,				0x80000b00, 0, 0x4d},
+	{SCORPION_SMC,				0x800b0000, 0, 0x4e},
+	{SCORPION_PREFETCH_ABORT,		0x8b000000, 0, 0x4f},
+	{SCORPION_INDEX_CHECK,			0x8000000c, 0, 0x4c},
+	{SCORPION_NULL_CHECK,			0x80000c00, 0, 0x4d},
+	{SCORPION_ICIMVAU_IMPL_ICIALLU,		0x8000000d, 0, 0x4c},
+	{SCORPION_NONICIALLU_BTAC_INV,		0x80000d00, 0, 0x4d},
+	{SCORPION_IMPL_ICIALLU,			0x800d0000, 0, 0x4e},
+	{SCORPION_EXPL_ICIALLU,			0x8d000000, 0, 0x4f},
+
+	{SCORPION_SPIPE_ONLY_CYCLES,		0x80000600, 1, 0x51},
+	{SCORPION_XPIPE_ONLY_CYCLES,		0x80060000, 1, 0x52},
+	{SCORPION_DUAL_CYCLES,			0x86000000, 1, 0x53},
+	{SCORPION_DISPATCH_ANY_CYCLES,		0x89000000, 1, 0x53},
+	{SCORPION_FIFO_FULLBLK_CMT,		0x8000000d, 1, 0x50},
+	{SCORPION_FAIL_COND_INST,		0x800d0000, 1, 0x52},
+	{SCORPION_PASS_COND_INST,		0x8d000000, 1, 0x53},
+	{SCORPION_ALLOW_VU_CLK,			0x8000000e, 1, 0x50},
+	{SCORPION_VU_IDLE,			0x80000e00, 1, 0x51},
+	{SCORPION_ALLOW_L2_CLK,			0x800e0000, 1, 0x52},
+	{SCORPION_L2_IDLE,			0x8e000000, 1, 0x53},
+
+	{SCORPION_DTLB_IMPL_INV_SCTLR_DACR,	0x80000001, 2, 0x54},
+	{SCORPION_DTLB_EXPL_INV,		0x80000100, 2, 0x55},
+	{SCORPION_DTLB_MISS,			0x80010000, 2, 0x56},
+	{SCORPION_DTLB_ACCESS,			0x81000000, 2, 0x57},
+	{SCORPION_ITLB_MISS,			0x80000200, 2, 0x55},
+	{SCORPION_ITLB_IMPL_INV,		0x80020000, 2, 0x56},
+	{SCORPION_ITLB_EXPL_INV,		0x82000000, 2, 0x57},
+	{SCORPION_UTLB_D_MISS,			0x80000003, 2, 0x54},
+	{SCORPION_UTLB_D_ACCESS,		0x80000300, 2, 0x55},
+	{SCORPION_UTLB_I_MISS,			0x80030000, 2, 0x56},
+	{SCORPION_UTLB_I_ACCESS,		0x83000000, 2, 0x57},
+	{SCORPION_UTLB_INV_ASID,		0x80000400, 2, 0x55},
+	{SCORPION_UTLB_INV_MVA,			0x80040000, 2, 0x56},
+	{SCORPION_UTLB_INV_ALL,			0x84000000, 2, 0x57},
+	{SCORPION_S2_HOLD_RDQ_UNAVAIL,		0x80000800, 2, 0x55},
+	{SCORPION_S2_HOLD,			0x88000000, 2, 0x57},
+	{SCORPION_S2_HOLD_DEV_OP,		0x80000900, 2, 0x55},
+	{SCORPION_S2_HOLD_ORDER,		0x80090000, 2, 0x56},
+	{SCORPION_S2_HOLD_BARRIER,		0x89000000, 2, 0x57},
+
+	{SCORPION_VIU_DUAL_CYCLE,		0x80000001, 4, 0x5c},
+	{SCORPION_VIU_SINGLE_CYCLE,		0x80000100, 4, 0x5d},
+	{SCORPION_VX_PIPE_WAR_STALL_CYCLES,	0x80000005, 4, 0x5c},
+	{SCORPION_VX_PIPE_WAW_STALL_CYCLES,	0x80000500, 4, 0x5d},
+	{SCORPION_VX_PIPE_RAW_STALL_CYCLES,	0x80050000, 4, 0x5e},
+	{SCORPION_VX_PIPE_LOAD_USE_STALL,	0x80000007, 4, 0x5c},
+	{SCORPION_VS_PIPE_WAR_STALL_CYCLES,	0x80000008, 4, 0x5c},
+	{SCORPION_VS_PIPE_WAW_STALL_CYCLES,	0x80000800, 4, 0x5d},
+	{SCORPION_VS_PIPE_RAW_STALL_CYCLES,	0x80080000, 4, 0x5e},
+	{SCORPION_EXCEPTIONS_INV_OPERATION,	0x8000000b, 4, 0x5c},
+	{SCORPION_EXCEPTIONS_DIV_BY_ZERO,	0x80000b00, 4, 0x5d},
+	{SCORPION_COND_INST_FAIL_VX_PIPE,	0x800b0000, 4, 0x5e},
+	{SCORPION_COND_INST_FAIL_VS_PIPE,	0x8b000000, 4, 0x5f},
+	{SCORPION_EXCEPTIONS_OVERFLOW,		0x8000000c, 4, 0x5c},
+	{SCORPION_EXCEPTIONS_UNDERFLOW,		0x80000c00, 4, 0x5d},
+	{SCORPION_EXCEPTIONS_DENORM,		0x8c000000, 4, 0x5f},
+
+#ifdef CONFIG_MSM_SMP
+	{SCORPIONMP_NUM_BARRIERS,		0x80000e00, 3, 0x59},
+	{SCORPIONMP_BARRIER_CYCLES,		0x800e0000, 3, 0x5a},
+#else
+	{SCORPION_BANK_AB_HIT,			0x80000001, 3, 0x58},
+	{SCORPION_BANK_AB_ACCESS,		0x80000100, 3, 0x59},
+	{SCORPION_BANK_CD_HIT,			0x80010000, 3, 0x5a},
+	{SCORPION_BANK_CD_ACCESS,		0x81000000, 3, 0x5b},
+	{SCORPION_BANK_AB_DSIDE_HIT,		0x80000002, 3, 0x58},
+	{SCORPION_BANK_AB_DSIDE_ACCESS,		0x80000200, 3, 0x59},
+	{SCORPION_BANK_CD_DSIDE_HIT,		0x80020000, 3, 0x5a},
+	{SCORPION_BANK_CD_DSIDE_ACCESS,		0x82000000, 3, 0x5b},
+	{SCORPION_BANK_AB_ISIDE_HIT,		0x80000003, 3, 0x58},
+	{SCORPION_BANK_AB_ISIDE_ACCESS,		0x80000300, 3, 0x59},
+	{SCORPION_BANK_CD_ISIDE_HIT,		0x80030000, 3, 0x5a},
+	{SCORPION_BANK_CD_ISIDE_ACCESS,		0x83000000, 3, 0x5b},
+	{SCORPION_ISIDE_RD_WAIT,		0x80000009, 3, 0x58},
+	{SCORPION_DSIDE_RD_WAIT,		0x80090000, 3, 0x5a},
+	{SCORPION_BANK_BYPASS_WRITE,		0x8000000a, 3, 0x58},
+	{SCORPION_BANK_AB_NON_CASTOUT,		0x8000000c, 3, 0x58},
+	{SCORPION_BANK_AB_L2_CASTOUT,		0x80000c00, 3, 0x59},
+	{SCORPION_BANK_CD_NON_CASTOUT,		0x800c0000, 3, 0x5a},
+	{SCORPION_BANK_CD_L2_CASTOUT,		0x8c000000, 3, 0x5b},
+#endif
+};
+
+static unsigned int get_scorpion_evtinfo(unsigned int scorpion_evt_type,
+					struct scorpion_evt *evtinfo)
+{
+	u32 idx;
+	u8 prefix;
+	u8 reg;
+	u8 code;
+	u8 group;
+
+	prefix = (scorpion_evt_type & 0xF0000) >> 16;
+	if (prefix == SCORPION_EVT_PREFIX) {
+		reg   = (scorpion_evt_type & 0x0F000) >> 12;
+		code  = (scorpion_evt_type & 0x00FF0) >> 4;
+		group =  scorpion_evt_type & 0x0000F;
+
+		if ((group > 3) || (reg > SCORPION_MAX_L1_REG))
+			return -EINVAL;
+
+		evtinfo->group_setval = 0x80000000 | (code << (group * 8));
+		evtinfo->groupcode = reg;
+		evtinfo->armv7_evt_type = scorpion_evt_type_base[reg] | group;
+
+		return evtinfo->armv7_evt_type;
+	}
+
+	if (scorpion_evt_type < SCORPION_EVT_START_IDX || scorpion_evt_type >=
+		(ARRAY_SIZE(scorpion_event) + SCORPION_EVT_START_IDX))
+		return -EINVAL;
+
+	idx = scorpion_evt_type - SCORPION_EVT_START_IDX;
+	if (scorpion_event[idx].scorpion_evt_type == scorpion_evt_type) {
+		evtinfo->group_setval = scorpion_event[idx].group_setval;
+		evtinfo->groupcode = scorpion_event[idx].groupcode;
+		evtinfo->armv7_evt_type = scorpion_event[idx].armv7_evt_type;
+		return scorpion_event[idx].armv7_evt_type;
+	}
+	return -EINVAL;
+}
+
+static u32 scorpion_read_lpm0(void)
+{
+	u32 val;
+
+	asm volatile("mrc p15, 0, %0, c15, c0, 0" : "=r" (val));
+	return val;
+}
+
+static void scorpion_write_lpm0(u32 val)
+{
+	asm volatile("mcr p15, 0, %0, c15, c0, 0" : : "r" (val));
+}
+
+static u32 scorpion_read_lpm1(void)
+{
+	u32 val;
+
+	asm volatile("mrc p15, 1, %0, c15, c0, 0" : "=r" (val));
+	return val;
+}
+
+static void scorpion_write_lpm1(u32 val)
+{
+	asm volatile("mcr p15, 1, %0, c15, c0, 0" : : "r" (val));
+}
+
+static u32 scorpion_read_lpm2(void)
+{
+	u32 val;
+
+	asm volatile("mrc p15, 2, %0, c15, c0, 0" : "=r" (val));
+	return val;
+}
+
+static void scorpion_write_lpm2(u32 val)
+{
+	asm volatile("mcr p15, 2, %0, c15, c0, 0" : : "r" (val));
+}
+
+static u32 scorpion_read_l2lpm(void)
+{
+	u32 val;
+
+	asm volatile("mrc p15, 3, %0, c15, c2, 0" : "=r" (val));
+	return val;
+}
+
+static void scorpion_write_l2lpm(u32 val)
+{
+	asm volatile("mcr p15, 3, %0, c15, c2, 0" : : "r" (val));
+}
+
+static u32 scorpion_read_vlpm(void)
+{
+	u32 val;
+
+	asm volatile("mrc p10, 7, %0, c11, c0, 0" : "=r" (val));
+	return val;
+}
+
+static void scorpion_write_vlpm(u32 val)
+{
+	asm volatile("mcr p10, 7, %0, c11, c0, 0" : : "r" (val));
+}
+
+/*
+ * The Scorpion processor supports performance monitoring for Venum unit.
+ * In order to access the performance monitor registers corresponding to
+ * VFP, CPACR and FPEXC registers need to be set up beforehand.
+ * Also, they need to be recovered once the access is done.
+ * This is the reason for having pre and post functions
+ */
+
+static DEFINE_PER_CPU(u32, venum_orig_val);
+static DEFINE_PER_CPU(u32, fp_orig_val);
+
+static void scorpion_pre_vlpm(void)
+{
+	u32 venum_new_val;
+	u32 fp_new_val;
+	u32 v_orig_val;
+	u32 f_orig_val;
+
+	/* CPACR Enable CP10 access */
+	v_orig_val = get_copro_access();
+	venum_new_val = v_orig_val | CPACC_SVC(10);
+	set_copro_access(venum_new_val);
+	/* Store orig venum val */
+	__get_cpu_var(venum_orig_val) = v_orig_val;
+
+	/* Enable FPEXC */
+	f_orig_val = fmrx(FPEXC);
+	fp_new_val = f_orig_val | FPEXC_EN;
+	fmxr(FPEXC, fp_new_val);
+	/* Store orig fp val */
+	__get_cpu_var(fp_orig_val) = f_orig_val;
+}
+
+static void scorpion_post_vlpm(void)
+{
+	/* Restore FPEXC */
+	fmxr(FPEXC, __get_cpu_var(fp_orig_val));
+	isb();
+	/* Restore CPACR */
+	set_copro_access(__get_cpu_var(venum_orig_val));
+}
+
+struct scorpion_access_funcs {
+	u32 (*read) (void);
+	void (*write) (u32);
+	void (*pre) (void);
+	void (*post) (void);
+};
+
+/*
+ * The scorpion_functions array is used to set up the event register codes
+ * based on the group to which an event belongs to.
+ * Having the following array modularizes the code for doing that.
+ */
+struct scorpion_access_funcs scorpion_functions[] = {
+	{scorpion_read_lpm0, scorpion_write_lpm0, NULL, NULL},
+	{scorpion_read_lpm1, scorpion_write_lpm1, NULL, NULL},
+	{scorpion_read_lpm2, scorpion_write_lpm2, NULL, NULL},
+	{scorpion_read_l2lpm, scorpion_write_l2lpm, NULL, NULL},
+	{scorpion_read_vlpm, scorpion_write_vlpm, scorpion_pre_vlpm,
+		scorpion_post_vlpm},
+};
+
+static inline u32 scorpion_get_columnmask(u32 evt_code)
+{
+	const u32 columnmasks[] = {0xffffff00, 0xffff00ff, 0xff00ffff,
+					0x80ffffff};
+
+	return columnmasks[evt_code & 0x3];
+}
+
+static void scorpion_evt_setup(u32 gr, u32 setval, u32 evt_code)
+{
+	u32 val;
+
+	if (scorpion_functions[gr].pre)
+		scorpion_functions[gr].pre();
+	val = scorpion_get_columnmask(evt_code) & scorpion_functions[gr].read();
+	val = val | setval;
+	scorpion_functions[gr].write(val);
+	if (scorpion_functions[gr].post)
+		scorpion_functions[gr].post();
+}
+
+static void scorpion_clear_pmuregs(void)
+{
+	unsigned long flags;
+
+	scorpion_write_lpm0(0);
+	scorpion_write_lpm1(0);
+	scorpion_write_lpm2(0);
+	scorpion_write_l2lpm(0);
+	raw_spin_lock_irqsave(&pmu_lock, flags);
+	scorpion_pre_vlpm();
+	scorpion_write_vlpm(0);
+	scorpion_post_vlpm();
+	raw_spin_unlock_irqrestore(&pmu_lock, flags);
+}
+
+static void scorpion_clearpmu(u32 grp, u32 val, u32 evt_code)
+{
+	u32 orig_pmuval, new_pmuval;
+
+	if (scorpion_functions[grp].pre)
+		scorpion_functions[grp].pre();
+	orig_pmuval = scorpion_functions[grp].read();
+	val = val & ~scorpion_get_columnmask(evt_code);
+	new_pmuval = orig_pmuval & ~val;
+	scorpion_functions[grp].write(new_pmuval);
+	if (scorpion_functions[grp].post)
+		scorpion_functions[grp].post();
+}
+
+static void scorpion_pmu_disable_event(struct hw_perf_event *hwc, int idx)
+{
+	unsigned long flags;
+	u32 val = 0;
+	u32 gr;
+	unsigned long event;
+	struct scorpion_evt evtinfo;
+
+	/* Disable counter and interrupt */
+	raw_spin_lock_irqsave(&pmu_lock, flags);
+
+	/* Disable counter */
+	armv7_pmnc_disable_counter(idx);
+
+	/*
+	 * Clear lpm code (if destined for PMNx counters)
+	 * We don't need to set the event if it's a cycle count
+	 */
+	if (idx != ARMV7_CYCLE_COUNTER) {
+		val = hwc->config_base;
+		val &= SCORPION_EVTYPE_EVENT;
+
+		if (val > 0x40) {
+			event = get_scorpion_evtinfo(val, &evtinfo);
+			if (event == -EINVAL)
+				goto scorpion_dis_out;
+			val = evtinfo.group_setval;
+			gr = evtinfo.groupcode;
+			scorpion_clearpmu(gr, val, evtinfo.armv7_evt_type);
+		}
+	}
+	/* Disable interrupt for this counter */
+	armv7_pmnc_disable_intens(idx);
+
+scorpion_dis_out:
+	raw_spin_unlock_irqrestore(&pmu_lock, flags);
+}
+
+static void scorpion_pmu_enable_event(struct hw_perf_event *hwc, int idx)
+{
+	unsigned long flags;
+	u32 val = 0;
+	u32 gr;
+	unsigned long event;
+	struct scorpion_evt evtinfo;
+	unsigned long long prev_count = local64_read(&hwc->prev_count);
+
+	/*
+	 * Enable counter and interrupt, and set the counter to count
+	 * the event that we're interested in.
+	 */
+	raw_spin_lock_irqsave(&pmu_lock, flags);
+
+	/* Disable counter */
+	armv7_pmnc_disable_counter(idx);
+
+	/*
+	 * Set event (if destined for PMNx counters)
+	 * We don't need to set the event if it's a cycle count
+	 */
+	if (idx != ARMV7_CYCLE_COUNTER) {
+		val = hwc->config_base;
+		val &= SCORPION_EVTYPE_EVENT;
+
+		if (val < 0x40) {
+			armv7_pmnc_write_evtsel(idx, hwc->config_base);
+		} else {
+			event = get_scorpion_evtinfo(val, &evtinfo);
+
+			if (event == -EINVAL)
+				goto scorpion_out;
+			/*
+			 * Set event (if destined for PMNx counters)
+			 * We don't need to set the event if it's a cycle count
+			 */
+			armv7_pmnc_write_evtsel(idx, event);
+			val = 0x0;
+			asm volatile("mcr p15, 0, %0, c9, c15, 0" : :
+				"r" (val));
+			val = evtinfo.group_setval;
+			gr = evtinfo.groupcode;
+			scorpion_evt_setup(gr, val, evtinfo.armv7_evt_type);
+		}
+	}
+
+	/* Enable interrupt for this counter */
+	armv7_pmnc_enable_intens(idx);
+
+	/* Restore prev val */
+	armv7pmu_write_counter(idx, prev_count & COUNT_MASK);
+
+	/* Enable counter */
+	armv7_pmnc_enable_counter(idx);
+
+scorpion_out:
+	raw_spin_unlock_irqrestore(&pmu_lock, flags);
+}
+
+static void enable_irq_callback(void *info)
+{
+	int irq = *(unsigned int *)info;
+
+	enable_percpu_irq(irq, IRQ_TYPE_EDGE_RISING);
+}
+
+static void disable_irq_callback(void *info)
+{
+	int irq = *(unsigned int *)info;
+
+	disable_percpu_irq(irq);
+}
+
+static int
+msm_request_irq(int irq, irq_handler_t *handle_irq)
+{
+	int err = 0;
+	int cpu;
+
+	err = request_percpu_irq(irq, *handle_irq, "armpmu",
+			&cpu_hw_events);
+
+	if (!err) {
+		for_each_cpu(cpu, cpu_online_mask) {
+			smp_call_function_single(cpu,
+					enable_irq_callback, &irq, 1);
+		}
+	}
+
+	return err;
+}
+
+static void
+msm_free_irq(int irq)
+{
+	int cpu;
+
+	if (irq >= 0) {
+		for_each_cpu(cpu, cpu_online_mask) {
+			smp_call_function_single(cpu,
+					disable_irq_callback, &irq, 1);
+		}
+		free_percpu_irq(irq, &cpu_hw_events);
+	}
+}
+
+static void scorpion_pmu_reset(void *info)
+{
+	u32 idx, nb_cnt = armpmu->num_events;
+
+	/* Stop all counters and their interrupts */
+	for (idx = 1; idx < nb_cnt; ++idx) {
+		armv7_pmnc_disable_counter(idx);
+		armv7_pmnc_disable_intens(idx);
+	}
+
+	/* Clear all pmresrs */
+	scorpion_clear_pmuregs();
+
+	/* Reset irq stat reg */
+	armv7_pmnc_getreset_flags();
+
+	/* Reset all ctrs to 0 */
+	armv7_pmnc_write(ARMV7_PMNC_P | ARMV7_PMNC_C);
+}
+
+static struct arm_pmu scorpion_pmu = {
+	.handle_irq		= armv7pmu_handle_irq,
+	.request_pmu_irq	= msm_request_irq,
+	.free_pmu_irq		= msm_free_irq,
+	.enable			= scorpion_pmu_enable_event,
+	.disable		= scorpion_pmu_disable_event,
+	.read_counter		= armv7pmu_read_counter,
+	.write_counter		= armv7pmu_write_counter,
+	.raw_event_mask		= 0xFFFFF,
+	.get_event_idx		= armv7pmu_get_event_idx,
+	.start			= armv7pmu_start,
+	.stop			= armv7pmu_stop,
+	.reset			= scorpion_pmu_reset,
+	.max_period		= (1LLU << 32) - 1,
+};
+
+static const struct arm_pmu *__init armv7_scorpion_pmu_init(void)
+{
+	scorpion_pmu.id		= ARM_PERF_PMU_ID_SCORPION;
+	scorpion_pmu.name	= "ARMv7 Scorpion";
+	scorpion_pmu.cache_map	= &armv7_scorpion_perf_cache_map;
+	scorpion_pmu.event_map	= &armv7_scorpion_perf_map;
+	scorpion_pmu.num_events	= armv7_read_num_pmnc_events();
+	scorpion_clear_pmuregs();
+	return &scorpion_pmu;
+}
+
+static const struct arm_pmu *__init armv7_scorpionmp_pmu_init(void)
+{
+	scorpion_pmu.id		= ARM_PERF_PMU_ID_SCORPIONMP;
+	scorpion_pmu.name	= "ARMv7 Scorpion-MP";
+	scorpion_pmu.cache_map	= &armv7_scorpion_perf_cache_map;
+	scorpion_pmu.event_map	= &armv7_scorpion_perf_map;
+	scorpion_pmu.num_events	= armv7_read_num_pmnc_events();
+	scorpion_clear_pmuregs();
+	return &scorpion_pmu;
+}
+#else
+static const struct arm_pmu *__init armv7_scorpion_pmu_init(void)
+{
+	return NULL;
+}
+static const struct arm_pmu *__init armv7_scorpionmp_pmu_init(void)
+{
+	return NULL;
+}
+#endif	/* CONFIG_CPU_V7 */
diff --git a/arch/arm/kernel/perf_event_msm_krait.c b/arch/arm/kernel/perf_event_msm_krait.c
new file mode 100644
index 0000000..8dbd047
--- /dev/null
+++ b/arch/arm/kernel/perf_event_msm_krait.c
@@ -0,0 +1,640 @@
+/*
+ * Copyright (c) 2011-2012, Code Aurora Forum. 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/cp15.h>
+#include <asm/vfp.h>
+#include "../vfp/vfpinstr.h"
+
+#ifdef CONFIG_CPU_V7
+#define KRAIT_EVT_PREFIX 1
+#define KRAIT_VENUMEVT_PREFIX 2
+/*
+   event encoding:                prccg
+   p  = prefix (1 for Krait L1) (2 for Krait VeNum events)
+   r  = register
+   cc = code
+   g  = group
+*/
+
+#define KRAIT_L1_ICACHE_ACCESS 0x10011
+#define KRAIT_L1_ICACHE_MISS 0x10010
+
+#define KRAIT_P1_L1_ITLB_ACCESS 0x121b2
+#define KRAIT_P1_L1_DTLB_ACCESS 0x121c0
+
+#define KRAIT_P2_L1_ITLB_ACCESS 0x12222
+#define KRAIT_P2_L1_DTLB_ACCESS 0x12210
+
+#define KRAIT_EVENT_MASK 0xfffff
+#define KRAIT_MODE_EXCL_MASK 0xc0000000
+
+#define COUNT_MASK	0xffffffff
+
+u32 evt_type_base[][4] = {
+	{0x4c, 0x50, 0x54},		/* Pass 1 */
+	{0xcc, 0xd0, 0xd4, 0xd8},	/* Pass 2 */
+};
+
+#define KRAIT_MIDR_PASS1 0x510F04D0
+#define KRAIT_MIDR_MASK 0xfffffff0
+
+/*
+ * This offset is used to calculate the index
+ * into evt_type_base[][] and krait_functions[]
+ */
+#define VENUM_BASE_OFFSET 3
+
+/* Krait Pass 1 has 3 groups, Pass 2 has 4 */
+static u32 krait_ver, evt_index;
+static u32 krait_max_l1_reg;
+
+static const unsigned armv7_krait_perf_map[PERF_COUNT_HW_MAX] = {
+	[PERF_COUNT_HW_CPU_CYCLES]	    = ARMV7_PERFCTR_CPU_CYCLES,
+	[PERF_COUNT_HW_INSTRUCTIONS]	    = ARMV7_PERFCTR_INSTR_EXECUTED,
+	[PERF_COUNT_HW_CACHE_REFERENCES]    = HW_OP_UNSUPPORTED,
+	[PERF_COUNT_HW_CACHE_MISSES]	    = HW_OP_UNSUPPORTED,
+	[PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = ARMV7_PERFCTR_PC_WRITE,
+	[PERF_COUNT_HW_BRANCH_MISSES]	    = ARMV7_PERFCTR_PC_BRANCH_MIS_PRED,
+	[PERF_COUNT_HW_BUS_CYCLES]	    = ARMV7_PERFCTR_CLOCK_CYCLES,
+};
+
+static unsigned armv7_krait_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
+					  [PERF_COUNT_HW_CACHE_OP_MAX]
+					  [PERF_COUNT_HW_CACHE_RESULT_MAX] = {
+	[C(L1D)] = {
+		/*
+		 * The performance counters don't differentiate between read
+		 * and write accesses/misses so this isn't strictly correct,
+		 * but it's the best we can do. Writes and reads get
+		 * combined.
+		 */
+		[C(OP_READ)] = {
+			[C(RESULT_ACCESS)]	= ARMV7_PERFCTR_L1_DCACHE_ACCESS,
+			[C(RESULT_MISS)]	= ARMV7_PERFCTR_L1_DCACHE_REFILL,
+		},
+		[C(OP_WRITE)] = {
+			[C(RESULT_ACCESS)]	= ARMV7_PERFCTR_L1_DCACHE_ACCESS,
+			[C(RESULT_MISS)]	= ARMV7_PERFCTR_L1_DCACHE_REFILL,
+		},
+		[C(OP_PREFETCH)] = {
+			[C(RESULT_ACCESS)]	= CACHE_OP_UNSUPPORTED,
+			[C(RESULT_MISS)]	= CACHE_OP_UNSUPPORTED,
+		},
+	},
+	[C(L1I)] = {
+		[C(OP_READ)] = {
+			[C(RESULT_ACCESS)]	= KRAIT_L1_ICACHE_ACCESS,
+			[C(RESULT_MISS)]	= KRAIT_L1_ICACHE_MISS,
+		},
+		[C(OP_WRITE)] = {
+			[C(RESULT_ACCESS)]	= KRAIT_L1_ICACHE_ACCESS,
+			[C(RESULT_MISS)]	= KRAIT_L1_ICACHE_MISS,
+		},
+		[C(OP_PREFETCH)] = {
+			[C(RESULT_ACCESS)]	= CACHE_OP_UNSUPPORTED,
+			[C(RESULT_MISS)]	= CACHE_OP_UNSUPPORTED,
+		},
+	},
+	[C(LL)] = {
+		[C(OP_READ)] = {
+			[C(RESULT_ACCESS)]	= CACHE_OP_UNSUPPORTED,
+			[C(RESULT_MISS)]	= CACHE_OP_UNSUPPORTED,
+		},
+		[C(OP_WRITE)] = {
+			[C(RESULT_ACCESS)]	= CACHE_OP_UNSUPPORTED,
+			[C(RESULT_MISS)]	= CACHE_OP_UNSUPPORTED,
+		},
+		[C(OP_PREFETCH)] = {
+			[C(RESULT_ACCESS)]	= CACHE_OP_UNSUPPORTED,
+			[C(RESULT_MISS)]	= CACHE_OP_UNSUPPORTED,
+		},
+	},
+	[C(DTLB)] = {
+		[C(OP_READ)] = {
+			[C(RESULT_ACCESS)]	= CACHE_OP_UNSUPPORTED,
+			[C(RESULT_MISS)]	= CACHE_OP_UNSUPPORTED,
+		},
+		[C(OP_WRITE)] = {
+			[C(RESULT_ACCESS)]	= CACHE_OP_UNSUPPORTED,
+			[C(RESULT_MISS)]	= CACHE_OP_UNSUPPORTED,
+		},
+		[C(OP_PREFETCH)] = {
+			[C(RESULT_ACCESS)]	= CACHE_OP_UNSUPPORTED,
+			[C(RESULT_MISS)]	= CACHE_OP_UNSUPPORTED,
+		},
+	},
+	[C(ITLB)] = {
+		[C(OP_READ)] = {
+			[C(RESULT_ACCESS)]	= CACHE_OP_UNSUPPORTED,
+			[C(RESULT_MISS)]	= CACHE_OP_UNSUPPORTED,
+		},
+		[C(OP_WRITE)] = {
+			[C(RESULT_ACCESS)]	= CACHE_OP_UNSUPPORTED,
+			[C(RESULT_MISS)]	= CACHE_OP_UNSUPPORTED,
+		},
+		[C(OP_PREFETCH)] = {
+			[C(RESULT_ACCESS)]	= CACHE_OP_UNSUPPORTED,
+			[C(RESULT_MISS)]	= CACHE_OP_UNSUPPORTED,
+		},
+	},
+	[C(BPU)] = {
+		[C(OP_READ)] = {
+			[C(RESULT_ACCESS)]
+					= ARMV7_PERFCTR_PC_BRANCH_PRED,
+			[C(RESULT_MISS)]
+					= ARMV7_PERFCTR_PC_BRANCH_PRED,
+		},
+		[C(OP_WRITE)] = {
+			[C(RESULT_ACCESS)]
+					= ARMV7_PERFCTR_PC_BRANCH_PRED,
+			[C(RESULT_MISS)]
+					= ARMV7_PERFCTR_PC_BRANCH_PRED,
+		},
+		[C(OP_PREFETCH)] = {
+			[C(RESULT_ACCESS)]	= CACHE_OP_UNSUPPORTED,
+			[C(RESULT_MISS)]	= CACHE_OP_UNSUPPORTED,
+		},
+	},
+};
+
+static int krait_8960_map_event(struct perf_event *event)
+{
+	return map_cpu_event(event, &armv7_krait_perf_map,
+			&armv7_krait_perf_cache_map, 0xfffff);
+}
+
+struct krait_evt {
+	/*
+	 * The group_setval field corresponds to the value that the group
+	 * register needs to be set to. This value is calculated from the row
+	 * and column that the event belongs to in the event table
+	 */
+	u32 group_setval;
+
+	/*
+	 * The groupcode corresponds to the group that the event belongs to.
+	 * Krait has 3 groups of events PMRESR0, 1, 2
+	 * going from 0 to 2 in terms of the codes used
+	 */
+	u8 groupcode;
+
+	/*
+	 * The armv7_evt_type field corresponds to the armv7 defined event
+	 * code that the Krait events map to
+	 */
+	u32 armv7_evt_type;
+};
+
+static unsigned int get_krait_evtinfo(unsigned int krait_evt_type,
+					struct krait_evt *evtinfo)
+{
+	u8 prefix;
+	u8 reg;
+	u8 code;
+	u8 group;
+
+	prefix = (krait_evt_type & 0xF0000) >> 16;
+	reg = (krait_evt_type & 0x0F000) >> 12;
+	code = (krait_evt_type & 0x00FF0) >> 4;
+	group = krait_evt_type & 0x0000F;
+
+	if ((group > 3) || (reg > krait_max_l1_reg))
+		return -EINVAL;
+
+	if (prefix != KRAIT_EVT_PREFIX && prefix != KRAIT_VENUMEVT_PREFIX)
+		return -EINVAL;
+
+	if (prefix == KRAIT_VENUMEVT_PREFIX) {
+		if ((code & 0xe0) || krait_ver != 2)
+			return -EINVAL;
+		else
+			reg += VENUM_BASE_OFFSET;
+	}
+
+	evtinfo->group_setval = 0x80000000 | (code << (group * 8));
+	evtinfo->groupcode = reg;
+	evtinfo->armv7_evt_type = evt_type_base[evt_index][reg] | group;
+
+	return evtinfo->armv7_evt_type;
+}
+
+static u32 krait_read_pmresr0(void)
+{
+	u32 val;
+
+	asm volatile("mrc p15, 1, %0, c9, c15, 0" : "=r" (val));
+	return val;
+}
+
+static void krait_write_pmresr0(u32 val)
+{
+	asm volatile("mcr p15, 1, %0, c9, c15, 0" : : "r" (val));
+}
+
+static u32 krait_read_pmresr1(void)
+{
+	u32 val;
+
+	asm volatile("mrc p15, 1, %0, c9, c15, 1" : "=r" (val));
+	return val;
+}
+
+static void krait_write_pmresr1(u32 val)
+{
+	asm volatile("mcr p15, 1, %0, c9, c15, 1" : : "r" (val));
+}
+
+static u32 krait_read_pmresr2(void)
+{
+	u32 val;
+
+	asm volatile("mrc p15, 1, %0, c9, c15, 2" : "=r" (val));
+	return val;
+}
+
+static void krait_write_pmresr2(u32 val)
+{
+	asm volatile("mcr p15, 1, %0, c9, c15, 2" : : "r" (val));
+}
+
+static u32 krait_read_vmresr0(void)
+{
+	u32 val;
+
+	asm volatile ("mrc p10, 7, %0, c11, c0, 0" : "=r" (val));
+	return val;
+}
+
+static void krait_write_vmresr0(u32 val)
+{
+	asm volatile ("mcr p10, 7, %0, c11, c0, 0" : : "r" (val));
+}
+
+static DEFINE_PER_CPU(u32, venum_orig_val);
+static DEFINE_PER_CPU(u32, fp_orig_val);
+
+static void krait_pre_vmresr0(void)
+{
+	u32 venum_new_val;
+	u32 fp_new_val;
+	u32 v_orig_val;
+	u32 f_orig_val;
+
+	/* CPACR Enable CP10 and CP11 access */
+	v_orig_val = get_copro_access();
+	venum_new_val = v_orig_val | CPACC_SVC(10) | CPACC_SVC(11);
+	set_copro_access(venum_new_val);
+	/* Store orig venum val */
+	__get_cpu_var(venum_orig_val) = v_orig_val;
+
+	/* Enable FPEXC */
+	f_orig_val = fmrx(FPEXC);
+	fp_new_val = f_orig_val | FPEXC_EN;
+	fmxr(FPEXC, fp_new_val);
+	/* Store orig fp val */
+	__get_cpu_var(fp_orig_val) = f_orig_val;
+
+}
+
+static void krait_post_vmresr0(void)
+{
+	/* Restore FPEXC */
+	fmxr(FPEXC, __get_cpu_var(fp_orig_val));
+	isb();
+	/* Restore CPACR */
+	set_copro_access(__get_cpu_var(venum_orig_val));
+}
+
+struct krait_access_funcs {
+	u32 (*read) (void);
+	void (*write) (u32);
+	void (*pre) (void);
+	void (*post) (void);
+};
+
+/*
+ * The krait_functions array is used to set up the event register codes
+ * based on the group to which an event belongs.
+ * Having the following array modularizes the code for doing that.
+ */
+struct krait_access_funcs krait_functions[] = {
+	{krait_read_pmresr0, krait_write_pmresr0, NULL, NULL},
+	{krait_read_pmresr1, krait_write_pmresr1, NULL, NULL},
+	{krait_read_pmresr2, krait_write_pmresr2, NULL, NULL},
+	{krait_read_vmresr0, krait_write_vmresr0, krait_pre_vmresr0,
+	 krait_post_vmresr0},
+};
+
+static inline u32 krait_get_columnmask(u32 evt_code)
+{
+	const u32 columnmasks[] = {0xffffff00, 0xffff00ff, 0xff00ffff,
+					0x80ffffff};
+
+	return columnmasks[evt_code & 0x3];
+}
+
+static void krait_evt_setup(u32 gr, u32 setval, u32 evt_code)
+{
+	u32 val;
+
+	if (krait_functions[gr].pre)
+		krait_functions[gr].pre();
+
+	val = krait_get_columnmask(evt_code) & krait_functions[gr].read();
+	val = val | setval;
+	krait_functions[gr].write(val);
+
+	if (krait_functions[gr].post)
+		krait_functions[gr].post();
+}
+
+static void krait_clear_pmuregs(void)
+{
+	krait_write_pmresr0(0);
+	krait_write_pmresr1(0);
+	krait_write_pmresr2(0);
+
+	krait_pre_vmresr0();
+	krait_write_vmresr0(0);
+	krait_post_vmresr0();
+}
+
+static void krait_clearpmu(u32 grp, u32 val, u32 evt_code)
+{
+	u32 new_pmuval;
+
+	if (krait_functions[grp].pre)
+		krait_functions[grp].pre();
+
+	new_pmuval = krait_functions[grp].read() &
+		krait_get_columnmask(evt_code);
+	krait_functions[grp].write(new_pmuval);
+
+	if (krait_functions[grp].post)
+		krait_functions[grp].post();
+}
+
+static void krait_pmu_disable_event(struct hw_perf_event *hwc, int idx)
+{
+	unsigned long flags;
+	u32 val = 0;
+	u32 gr;
+	unsigned long event;
+	struct krait_evt evtinfo;
+	struct pmu_hw_events *events = cpu_pmu->get_hw_events();
+
+
+	/* Disable counter and interrupt */
+	raw_spin_lock_irqsave(&events->pmu_lock, flags);
+
+	/* Disable counter */
+	armv7_pmnc_disable_counter(idx);
+
+	/*
+	 * Clear pmresr code (if destined for PMNx counters)
+	 * We don't need to set the event if it's a cycle count
+	 */
+	if (idx != ARMV7_IDX_CYCLE_COUNTER) {
+		val = hwc->config_base;
+		val &= KRAIT_EVENT_MASK;
+
+		if (val > 0x40) {
+			event = get_krait_evtinfo(val, &evtinfo);
+			if (event == -EINVAL)
+				goto krait_dis_out;
+			val = evtinfo.group_setval;
+			gr = evtinfo.groupcode;
+			krait_clearpmu(gr, val, evtinfo.armv7_evt_type);
+		}
+	}
+	/* Disable interrupt for this counter */
+	armv7_pmnc_disable_intens(idx);
+
+krait_dis_out:
+	raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
+}
+
+static void krait_pmu_enable_event(struct hw_perf_event *hwc, int idx, int cpu)
+{
+	unsigned long flags;
+	u32 val = 0;
+	u32 gr;
+	unsigned long event;
+	struct krait_evt evtinfo;
+	unsigned long long prev_count = local64_read(&hwc->prev_count);
+	struct pmu_hw_events *events = cpu_pmu->get_hw_events();
+
+	/*
+	 * Enable counter and interrupt, and set the counter to count
+	 * the event that we're interested in.
+	 */
+	raw_spin_lock_irqsave(&events->pmu_lock, flags);
+
+	/* Disable counter */
+	armv7_pmnc_disable_counter(idx);
+
+	/*
+	 * Set event (if destined for PMNx counters)
+	 * We don't need to set the event if it's a cycle count
+	 */
+	if (idx != ARMV7_IDX_CYCLE_COUNTER) {
+		val = hwc->config_base;
+		val &= KRAIT_EVENT_MASK;
+
+		if (val < 0x40) {
+			armv7_pmnc_write_evtsel(idx, hwc->config_base);
+		} else {
+			event = get_krait_evtinfo(val, &evtinfo);
+
+			if (event == -EINVAL)
+				goto krait_out;
+
+			/* Restore Mode-exclusion bits */
+			event |= (hwc->config_base & KRAIT_MODE_EXCL_MASK);
+
+			/*
+			 * Set event (if destined for PMNx counters)
+			 * We don't need to set the event if it's a cycle count
+			 */
+			armv7_pmnc_write_evtsel(idx, event);
+			val = 0x0;
+			asm volatile("mcr p15, 0, %0, c9, c15, 0" : :
+				"r" (val));
+			val = evtinfo.group_setval;
+			gr = evtinfo.groupcode;
+			krait_evt_setup(gr, val, evtinfo.armv7_evt_type);
+		}
+	}
+
+	/* Enable interrupt for this counter */
+	armv7_pmnc_enable_intens(idx);
+
+	/* Restore prev val */
+	armv7pmu_write_counter(idx, prev_count & COUNT_MASK);
+
+	/* Enable counter */
+	armv7_pmnc_enable_counter(idx);
+
+krait_out:
+	raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
+}
+
+static void krait_pmu_reset(void *info)
+{
+	u32 idx, nb_cnt = cpu_pmu->num_events;
+
+	/* Stop all counters and their interrupts */
+	for (idx = 1; idx < nb_cnt; ++idx) {
+		armv7_pmnc_disable_counter(idx);
+		armv7_pmnc_disable_intens(idx);
+	}
+
+	/* Clear all pmresrs */
+	krait_clear_pmuregs();
+
+	/* Reset irq stat reg */
+	armv7_pmnc_getreset_flags();
+
+	/* Reset all ctrs to 0 */
+	armv7_pmnc_write(ARMV7_PMNC_P | ARMV7_PMNC_C);
+}
+
+static void enable_irq_callback(void *info)
+{
+        int irq = *(unsigned int *)info;
+
+        enable_percpu_irq(irq, IRQ_TYPE_EDGE_RISING);
+}
+
+static void disable_irq_callback(void *info)
+{
+        int irq = *(unsigned int *)info;
+
+        disable_percpu_irq(irq);
+}
+
+static int
+msm_request_irq(int irq, irq_handler_t *handle_irq)
+{
+        int err = 0;
+        int cpu;
+
+        err = request_percpu_irq(irq, *handle_irq, "krait-l1-armpmu",
+                        &cpu_hw_events);
+
+        if (!err) {
+                for_each_cpu(cpu, cpu_online_mask) {
+                        smp_call_function_single(cpu,
+                                        enable_irq_callback, &irq, 1);
+                }
+        }
+
+        return err;
+}
+
+static void
+msm_free_irq(int irq)
+{
+        int cpu;
+
+        if (irq >= 0) {
+                for_each_cpu(cpu, cpu_online_mask) {
+                        smp_call_function_single(cpu,
+                                        disable_irq_callback, &irq, 1);
+                }
+                free_percpu_irq(irq, &cpu_hw_events);
+        }
+}
+
+static struct arm_pmu krait_pmu = {
+	.handle_irq		= armv7pmu_handle_irq,
+	.request_pmu_irq	= msm_request_irq,
+	.free_pmu_irq		= msm_free_irq,
+	.enable			= krait_pmu_enable_event,
+	.disable		= krait_pmu_disable_event,
+	.read_counter		= armv7pmu_read_counter,
+	.write_counter		= armv7pmu_write_counter,
+	.get_event_idx		= armv7pmu_get_event_idx,
+	.start			= armv7pmu_start,
+	.stop			= armv7pmu_stop,
+	.reset			= krait_pmu_reset,
+	.max_period		= (1LLU << 32) - 1,
+};
+
+int get_krait_ver(void)
+{
+	int ver = 0;
+	int midr = read_cpuid_id();
+
+	if ((midr & KRAIT_MIDR_MASK) != KRAIT_MIDR_PASS1)
+		ver = 2;
+
+	pr_debug("krait_ver: %d, midr: %x\n", ver, midr);
+
+	return ver;
+}
+
+static struct arm_pmu *__init armv7_krait_pmu_init(void)
+{
+	krait_pmu.id		= ARM_PERF_PMU_ID_KRAIT;
+	krait_pmu.name	        = "ARMv7 Krait";
+	krait_pmu.map_event	= krait_8960_map_event;
+	krait_pmu.num_events	= armv7_read_num_pmnc_events();
+	krait_clear_pmuregs();
+
+	krait_ver = get_krait_ver();
+
+	if (krait_ver > 0) {
+		evt_index = 1;
+		krait_max_l1_reg = 3;
+
+		krait_pmu.set_event_filter = armv7pmu_set_event_filter,
+
+		armv7_krait_perf_cache_map[C(ITLB)]
+			[C(OP_READ)]
+			[C(RESULT_ACCESS)] = KRAIT_P2_L1_ITLB_ACCESS;
+		armv7_krait_perf_cache_map[C(ITLB)]
+			[C(OP_WRITE)]
+			[C(RESULT_ACCESS)] = KRAIT_P2_L1_ITLB_ACCESS;
+		armv7_krait_perf_cache_map[C(DTLB)]
+			[C(OP_READ)]
+			[C(RESULT_ACCESS)] = KRAIT_P2_L1_DTLB_ACCESS;
+		armv7_krait_perf_cache_map[C(DTLB)]
+			[C(OP_WRITE)]
+			[C(RESULT_ACCESS)] = KRAIT_P2_L1_DTLB_ACCESS;
+	} else {
+		evt_index = 0;
+		krait_max_l1_reg = 2;
+		armv7_krait_perf_cache_map[C(ITLB)]
+			[C(OP_READ)]
+			[C(RESULT_ACCESS)] = KRAIT_P1_L1_ITLB_ACCESS;
+		armv7_krait_perf_cache_map[C(ITLB)]
+			[C(OP_WRITE)]
+			[C(RESULT_ACCESS)] = KRAIT_P1_L1_ITLB_ACCESS;
+		armv7_krait_perf_cache_map[C(DTLB)]
+			[C(OP_READ)]
+			[C(RESULT_ACCESS)] = KRAIT_P1_L1_DTLB_ACCESS;
+		armv7_krait_perf_cache_map[C(DTLB)]
+			[C(OP_WRITE)]
+			[C(RESULT_ACCESS)] = KRAIT_P1_L1_DTLB_ACCESS;
+	}
+
+	return &krait_pmu;
+}
+
+#else
+static const struct arm_pmu *__init armv7_krait_pmu_init(void)
+{
+	return NULL;
+}
+#endif	/* CONFIG_CPU_V7 */
diff --git a/arch/arm/kernel/perf_event_msm_krait_l2.c b/arch/arm/kernel/perf_event_msm_krait_l2.c
new file mode 100644
index 0000000..baa05ac
--- /dev/null
+++ b/arch/arm/kernel/perf_event_msm_krait_l2.c
@@ -0,0 +1,667 @@
+/*
+ * Copyright (c) 2011, 2012 Code Aurora Forum. 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.
+ */
+#ifdef CONFIG_ARCH_MSM_KRAIT
+
+#include <linux/irq.h>
+
+#include <mach/msm-krait-l2-accessors.h>
+
+#define MAX_L2_PERIOD	((1ULL << 32) - 1)
+#define MAX_KRAIT_L2_CTRS 5
+
+#define L2PMCCNTR 0x409
+#define L2PMCCNTCR 0x408
+#define L2PMCCNTSR 0x40A
+#define L2CYCLE_CTR_BIT 31
+#define L2CYCLE_CTR_EVENT_IDX 4
+#define L2CYCLE_CTR_RAW_CODE 0xfe
+
+#define L2PMOVSR	0x406
+
+#define L2PMCR	0x400
+#define L2PMCR_RESET_ALL	0x6
+#define L2PMCR_GLOBAL_ENABLE	0x1
+#define L2PMCR_GLOBAL_DISABLE	0x0
+
+#define L2PMCNTENSET	0x403
+#define L2PMCNTENCLR	0x402
+
+#define L2PMINTENSET	0x405
+#define L2PMINTENCLR	0x404
+
+#define IA_L2PMXEVCNTCR_BASE	0x420
+#define IA_L2PMXEVTYPER_BASE	0x424
+#define IA_L2PMRESX_BASE	0x410
+#define IA_L2PMXEVFILTER_BASE	0x423
+#define IA_L2PMXEVCNTR_BASE	0x421
+
+/* event format is -e rsRCCG See get_event_desc() */
+
+#define EVENT_REG_MASK		0xf000
+#define EVENT_GROUPSEL_MASK	0x000f
+#define	EVENT_GROUPCODE_MASK	0x0ff0
+#define EVENT_REG_SHIFT		12
+#define EVENT_GROUPCODE_SHIFT	4
+
+#define	RESRX_VALUE_EN	0x80000000
+
+static struct platform_device *l2_pmu_device;
+
+struct hw_krait_l2_pmu {
+	struct perf_event *events[MAX_KRAIT_L2_CTRS];
+	unsigned long active_mask[BITS_TO_LONGS(MAX_KRAIT_L2_CTRS)];
+	raw_spinlock_t lock;
+};
+
+struct hw_krait_l2_pmu hw_krait_l2_pmu;
+
+struct event_desc {
+	int event_groupsel;
+	int event_reg;
+	int event_group_code;
+};
+
+void get_event_desc(u64 config, struct event_desc *evdesc)
+{
+	/* L2PMEVCNTRX */
+	evdesc->event_reg = (config & EVENT_REG_MASK) >> EVENT_REG_SHIFT;
+	/* Group code (row ) */
+	evdesc->event_group_code =
+	    (config & EVENT_GROUPCODE_MASK) >> EVENT_GROUPCODE_SHIFT;
+	/* Group sel (col) */
+	evdesc->event_groupsel = (config & EVENT_GROUPSEL_MASK);
+
+	pr_debug("%s: reg: %x, group_code: %x, groupsel: %x\n", __func__,
+		 evdesc->event_reg, evdesc->event_group_code,
+		 evdesc->event_groupsel);
+}
+
+static void set_evcntcr(int ctr)
+{
+	u32 evtcr_reg = (ctr * 16) + IA_L2PMXEVCNTCR_BASE;
+
+	set_l2_indirect_reg(evtcr_reg, 0x0);
+}
+
+static void set_evtyper(int event_groupsel, int event_reg, int ctr)
+{
+	u32 evtype_reg = (ctr * 16) + IA_L2PMXEVTYPER_BASE;
+	u32 evtype_val = event_groupsel + (4 * event_reg);
+
+	set_l2_indirect_reg(evtype_reg, evtype_val);
+}
+
+static void set_evres(int event_groupsel, int event_reg, int event_group_code)
+{
+	u32 group_reg = event_reg + IA_L2PMRESX_BASE;
+	u32 group_val =
+		RESRX_VALUE_EN | (event_group_code << (8 * event_groupsel));
+	u32 resr_val;
+	u32 group_byte = 0xff;
+	u32 group_mask = ~(group_byte << (8 * event_groupsel));
+
+	resr_val = get_l2_indirect_reg(group_reg);
+	resr_val &= group_mask;
+	resr_val |= group_val;
+
+	set_l2_indirect_reg(group_reg, resr_val);
+}
+
+static void set_evfilter_task_mode(int ctr)
+{
+	u32 filter_reg = (ctr * 16) + IA_L2PMXEVFILTER_BASE;
+	u32 filter_val = 0x000f0030 | 1 << smp_processor_id();
+
+	set_l2_indirect_reg(filter_reg, filter_val);
+}
+
+static void set_evfilter_sys_mode(int ctr)
+{
+	u32 filter_reg = (ctr * 16) + IA_L2PMXEVFILTER_BASE;
+	u32 filter_val = 0x000f003f;
+
+	set_l2_indirect_reg(filter_reg, filter_val);
+}
+
+static void enable_intenset(u32 idx)
+{
+	if (idx == L2CYCLE_CTR_EVENT_IDX)
+		set_l2_indirect_reg(L2PMINTENSET, 1 << L2CYCLE_CTR_BIT);
+	else
+		set_l2_indirect_reg(L2PMINTENSET, 1 << idx);
+}
+
+static void disable_intenclr(u32 idx)
+{
+	if (idx == L2CYCLE_CTR_EVENT_IDX)
+		set_l2_indirect_reg(L2PMINTENCLR, 1 << L2CYCLE_CTR_BIT);
+	else
+		set_l2_indirect_reg(L2PMINTENCLR, 1 << idx);
+}
+
+static void enable_counter(u32 idx)
+{
+	if (idx == L2CYCLE_CTR_EVENT_IDX)
+		set_l2_indirect_reg(L2PMCNTENSET, 1 << L2CYCLE_CTR_BIT);
+	else
+		set_l2_indirect_reg(L2PMCNTENSET, 1 << idx);
+}
+
+static void disable_counter(u32 idx)
+{
+	if (idx == L2CYCLE_CTR_EVENT_IDX)
+		set_l2_indirect_reg(L2PMCNTENCLR, 1 << L2CYCLE_CTR_BIT);
+	else
+		set_l2_indirect_reg(L2PMCNTENCLR, 1 << idx);
+}
+
+static u64 read_counter(u32 idx)
+{
+	u32 val;
+	u32 counter_reg = (idx * 16) + IA_L2PMXEVCNTR_BASE;
+
+	if (idx == L2CYCLE_CTR_EVENT_IDX)
+		val = get_l2_indirect_reg(L2PMCCNTR);
+	else
+		val = get_l2_indirect_reg(counter_reg);
+
+	return val;
+}
+
+static void write_counter(u32 idx, u32 val)
+{
+	u32 counter_reg = (idx * 16) + IA_L2PMXEVCNTR_BASE;
+
+	if (idx == L2CYCLE_CTR_EVENT_IDX)
+		set_l2_indirect_reg(L2PMCCNTR, val);
+	else
+		set_l2_indirect_reg(counter_reg, val);
+}
+
+static int
+pmu_event_set_period(struct perf_event *event,
+		     struct hw_perf_event *hwc, int idx)
+{
+	s64 left = local64_read(&hwc->period_left);
+	s64 period = hwc->sample_period;
+	int ret = 0;
+
+	if (unlikely(left <= -period)) {
+		left = period;
+		local64_set(&hwc->period_left, left);
+		hwc->last_period = period;
+		ret = 1;
+	}
+
+	if (unlikely(left <= 0)) {
+		left += period;
+		local64_set(&hwc->period_left, left);
+		hwc->last_period = period;
+		ret = 1;
+	}
+
+	if (left > (s64) MAX_L2_PERIOD)
+		left = MAX_L2_PERIOD;
+
+	local64_set(&hwc->prev_count, (u64)-left);
+
+	write_counter(idx, (u64) (-left) & 0xffffffff);
+
+	perf_event_update_userpage(event);
+
+	return ret;
+}
+
+static u64
+pmu_event_update(struct perf_event *event, struct hw_perf_event *hwc, int idx,
+		 int overflow)
+{
+	u64 prev_raw_count, new_raw_count;
+	u64 delta;
+
+again:
+	prev_raw_count = local64_read(&hwc->prev_count);
+	new_raw_count = read_counter(idx);
+
+	if (local64_cmpxchg(&hwc->prev_count, prev_raw_count,
+			    new_raw_count) != prev_raw_count)
+		goto again;
+
+	new_raw_count &= MAX_L2_PERIOD;
+	prev_raw_count &= MAX_L2_PERIOD;
+
+	if (overflow)
+		delta = MAX_L2_PERIOD - prev_raw_count + new_raw_count;
+	else
+		delta = new_raw_count - prev_raw_count;
+
+	local64_add(delta, &event->count);
+	local64_sub(delta, &hwc->period_left);
+
+	pr_debug("%s: new: %lld, prev: %lld, event: %ld count: %lld\n",
+		 __func__, new_raw_count, prev_raw_count,
+		 hwc->config_base, local64_read(&event->count));
+
+	return new_raw_count;
+}
+
+static void krait_l2_read(struct perf_event *event)
+{
+	struct hw_perf_event *hwc = &event->hw;
+
+	pmu_event_update(event, hwc, hwc->idx, 0);
+}
+
+static void krait_l2_stop_counter(struct perf_event *event, int flags)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	int idx = hwc->idx;
+
+	if (!(hwc->state & PERF_HES_STOPPED)) {
+		disable_intenclr(idx);
+		disable_counter(idx);
+
+		pmu_event_update(event, hwc, idx, 0);
+		hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE;
+	}
+
+	pr_debug("%s: event: %ld ctr: %d stopped\n", __func__, hwc->config_base,
+		 idx);
+}
+
+static void krait_l2_start_counter(struct perf_event *event, int flags)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	int idx = hwc->idx;
+	struct event_desc evdesc;
+
+	if (flags & PERF_EF_RELOAD)
+		WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE));
+
+	hwc->state = 0;
+
+	pmu_event_set_period(event, hwc, idx);
+
+	if (hwc->config_base == L2CYCLE_CTR_RAW_CODE)
+		goto out;
+
+	set_evcntcr(idx);
+
+	memset(&evdesc, 0, sizeof(evdesc));
+
+	get_event_desc(hwc->config_base, &evdesc);
+
+	set_evtyper(evdesc.event_groupsel, evdesc.event_reg, idx);
+
+	set_evres(evdesc.event_groupsel, evdesc.event_reg,
+		  evdesc.event_group_code);
+
+	if (event->cpu < 0)
+		set_evfilter_task_mode(idx);
+	else
+		set_evfilter_sys_mode(idx);
+
+out:
+	enable_intenset(idx);
+	enable_counter(idx);
+
+	pr_debug
+	    ("%s: ctr: %d group: %ld group_code: %lld started from cpu:%d\n",
+	     __func__, idx, hwc->config_base, hwc->config, smp_processor_id());
+}
+
+static void krait_l2_del_event(struct perf_event *event, int flags)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	int idx = hwc->idx;
+	unsigned long iflags;
+
+	raw_spin_lock_irqsave(&hw_krait_l2_pmu.lock, iflags);
+
+	clear_bit(idx, (long unsigned int *)(&hw_krait_l2_pmu.active_mask));
+
+	krait_l2_stop_counter(event, PERF_EF_UPDATE);
+	hw_krait_l2_pmu.events[idx] = NULL;
+	hwc->idx = -1;
+
+	raw_spin_unlock_irqrestore(&hw_krait_l2_pmu.lock, iflags);
+
+	pr_debug("%s: event: %ld deleted\n", __func__, hwc->config_base);
+
+	perf_event_update_userpage(event);
+}
+
+static int krait_l2_add_event(struct perf_event *event, int flags)
+{
+	int ctr = 0;
+	struct hw_perf_event *hwc = &event->hw;
+	unsigned long iflags;
+	int err = 0;
+
+	perf_pmu_disable(event->pmu);
+
+	raw_spin_lock_irqsave(&hw_krait_l2_pmu.lock, iflags);
+
+	/* Cycle counter has a resrvd index */
+	if (hwc->config_base == L2CYCLE_CTR_RAW_CODE) {
+		if (hw_krait_l2_pmu.events[L2CYCLE_CTR_EVENT_IDX]) {
+			pr_err("%s: Stale cycle ctr event ptr !\n", __func__);
+			err = -EINVAL;
+			goto out;
+		}
+		hwc->idx = L2CYCLE_CTR_EVENT_IDX;
+		hw_krait_l2_pmu.events[L2CYCLE_CTR_EVENT_IDX] = event;
+		set_bit(L2CYCLE_CTR_EVENT_IDX,
+			(long unsigned int *)&hw_krait_l2_pmu.active_mask);
+		goto skip_ctr_loop;
+	}
+
+	for (ctr = 0; ctr < MAX_KRAIT_L2_CTRS - 1; ctr++) {
+		if (!hw_krait_l2_pmu.events[ctr]) {
+			hwc->idx = ctr;
+			hw_krait_l2_pmu.events[ctr] = event;
+			set_bit(ctr,
+				(long unsigned int *)
+				&hw_krait_l2_pmu.active_mask);
+			break;
+		}
+	}
+
+	if (hwc->idx < 0) {
+		err = -ENOSPC;
+		pr_err("%s: No space for event: %llx!!\n", __func__,
+		       event->attr.config);
+		goto out;
+	}
+
+skip_ctr_loop:
+
+	disable_counter(hwc->idx);
+
+	hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE;
+
+	if (flags & PERF_EF_START)
+		krait_l2_start_counter(event, PERF_EF_RELOAD);
+
+	perf_event_update_userpage(event);
+
+	pr_debug("%s: event: %ld, ctr: %d added from cpu:%d\n",
+		 __func__, hwc->config_base, hwc->idx, smp_processor_id());
+out:
+	raw_spin_unlock_irqrestore(&hw_krait_l2_pmu.lock, iflags);
+
+	/* Resume the PMU even if this event could not be added */
+	perf_pmu_enable(event->pmu);
+
+	return err;
+}
+
+static void krait_l2_pmu_enable(struct pmu *pmu)
+{
+	isb();
+	set_l2_indirect_reg(L2PMCR, L2PMCR_GLOBAL_ENABLE);
+}
+
+static void krait_l2_pmu_disable(struct pmu *pmu)
+{
+	set_l2_indirect_reg(L2PMCR, L2PMCR_GLOBAL_DISABLE);
+	isb();
+}
+
+u32 get_reset_pmovsr(void)
+{
+	int val;
+
+	val = get_l2_indirect_reg(L2PMOVSR);
+	/* reset it */
+	val &= 0xffffffff;
+	set_l2_indirect_reg(L2PMOVSR, val);
+
+	return val;
+}
+
+static irqreturn_t krait_l2_handle_irq(int irq_num, void *dev)
+{
+	unsigned long pmovsr;
+	struct perf_sample_data data;
+	struct pt_regs *regs;
+	struct perf_event *event;
+	struct hw_perf_event *hwc;
+	int bitp;
+	int idx = 0;
+
+	pmovsr = get_reset_pmovsr();
+
+	if (!(pmovsr & 0xffffffff))
+		return IRQ_NONE;
+
+	regs = get_irq_regs();
+
+	perf_sample_data_init(&data, 0);
+
+	raw_spin_lock(&hw_krait_l2_pmu.lock);
+
+	while (pmovsr) {
+		bitp = __ffs(pmovsr);
+
+		if (bitp == L2CYCLE_CTR_BIT)
+			idx = L2CYCLE_CTR_EVENT_IDX;
+		else
+			idx = bitp;
+
+		event = hw_krait_l2_pmu.events[idx];
+
+		if (!event)
+			goto next;
+
+		if (!test_bit(idx, hw_krait_l2_pmu.active_mask))
+			goto next;
+
+		hwc = &event->hw;
+		pmu_event_update(event, hwc, idx, 1);
+		data.period = event->hw.last_period;
+
+		if (!pmu_event_set_period(event, hwc, idx))
+			goto next;
+
+		if (perf_event_overflow(event, 0, &data, regs))
+			disable_counter(hwc->idx);
+next:
+		pmovsr &= (pmovsr - 1);
+	}
+
+	raw_spin_unlock(&hw_krait_l2_pmu.lock);
+
+	irq_work_run();
+
+	return IRQ_HANDLED;
+}
+
+static atomic_t active_l2_events = ATOMIC_INIT(0);
+static DEFINE_MUTEX(krait_pmu_reserve_mutex);
+
+static int pmu_reserve_hardware(void)
+{
+	int i, err = -ENODEV, irq;
+
+	l2_pmu_device = reserve_pmu(ARM_PMU_DEVICE_L2);
+
+	if (IS_ERR(l2_pmu_device)) {
+		pr_warning("unable to reserve pmu\n");
+		return PTR_ERR(l2_pmu_device);
+	}
+
+	if (l2_pmu_device->num_resources < 1) {
+		pr_err("no irqs for PMUs defined\n");
+		return -ENODEV;
+	}
+
+	if (strncmp(l2_pmu_device->name, "l2-arm-pmu", 6)) {
+		pr_err("Incorrect pdev reserved !\n");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < l2_pmu_device->num_resources; ++i) {
+		irq = platform_get_irq(l2_pmu_device, i);
+		if (irq < 0)
+			continue;
+
+		err = request_irq(irq, krait_l2_handle_irq,
+				  IRQF_DISABLED | IRQF_NOBALANCING,
+				  "krait-l2-pmu", NULL);
+		if (err) {
+			pr_warning("unable to request IRQ%d for Krait L2 perf "
+				   "counters\n", irq);
+			break;
+		}
+
+		irq_get_chip(irq)->irq_unmask(irq_get_irq_data(irq));
+	}
+
+	if (err) {
+		for (i = i - 1; i >= 0; --i) {
+			irq = platform_get_irq(l2_pmu_device, i);
+			if (irq >= 0)
+				free_irq(irq, NULL);
+		}
+		release_pmu(l2_pmu_device);
+		l2_pmu_device = NULL;
+	}
+
+	return err;
+}
+
+static void pmu_release_hardware(void)
+{
+	int i, irq;
+
+	for (i = l2_pmu_device->num_resources - 1; i >= 0; --i) {
+		irq = platform_get_irq(l2_pmu_device, i);
+		if (irq >= 0)
+			free_irq(irq, NULL);
+	}
+
+	krait_l2_pmu_disable(NULL);
+
+	release_pmu(l2_pmu_device);
+	l2_pmu_device = NULL;
+}
+
+static void pmu_perf_event_destroy(struct perf_event *event)
+{
+	if (atomic_dec_and_mutex_lock
+	    (&active_l2_events, &krait_pmu_reserve_mutex)) {
+		pmu_release_hardware();
+		mutex_unlock(&krait_pmu_reserve_mutex);
+	}
+}
+
+static int krait_l2_event_init(struct perf_event *event)
+{
+	int err = 0;
+	struct hw_perf_event *hwc = &event->hw;
+	int status = 0;
+
+	switch (event->attr.type) {
+	case PERF_TYPE_SHARED:
+		break;
+
+	default:
+		return -ENOENT;
+	}
+
+	hwc->idx = -1;
+
+	event->destroy = pmu_perf_event_destroy;
+
+	if (!atomic_inc_not_zero(&active_l2_events)) {
+		/* 0 active events */
+		mutex_lock(&krait_pmu_reserve_mutex);
+		err = pmu_reserve_hardware();
+		mutex_unlock(&krait_pmu_reserve_mutex);
+		if (!err)
+			atomic_inc(&active_l2_events);
+		else
+			return err;
+	}
+
+	hwc->config = 0;
+	hwc->event_base = 0;
+
+	/* Check if we came via perf default syms */
+	if (event->attr.config == PERF_COUNT_HW_L2_CYCLES)
+		hwc->config_base = L2CYCLE_CTR_RAW_CODE;
+	else
+		hwc->config_base = event->attr.config;
+
+	/* Only one CPU can control the cycle counter */
+	if (hwc->config_base == L2CYCLE_CTR_RAW_CODE) {
+		/* Check if its already running */
+		status = get_l2_indirect_reg(L2PMCCNTSR);
+		if (status == 0x2) {
+			err = -ENOSPC;
+			goto out;
+		}
+	}
+
+	if (!hwc->sample_period) {
+		hwc->sample_period = MAX_L2_PERIOD;
+		hwc->last_period = hwc->sample_period;
+		local64_set(&hwc->period_left, hwc->sample_period);
+	}
+
+	pr_debug("%s: event: %lld init'd\n", __func__, event->attr.config);
+
+out:
+	if (err < 0)
+		pmu_perf_event_destroy(event);
+
+	return err;
+}
+
+static struct pmu krait_l2_pmu = {
+	.pmu_enable = krait_l2_pmu_enable,
+	.pmu_disable = krait_l2_pmu_disable,
+	.event_init = krait_l2_event_init,
+	.add = krait_l2_add_event,
+	.del = krait_l2_del_event,
+	.start = krait_l2_start_counter,
+	.stop = krait_l2_stop_counter,
+	.read = krait_l2_read,
+};
+
+static const struct arm_pmu *__init krait_l2_pmu_init(void)
+{
+	/* Register our own PMU here */
+	perf_pmu_register(&krait_l2_pmu, "Krait L2", PERF_TYPE_SHARED);
+
+	memset(&hw_krait_l2_pmu, 0, sizeof(hw_krait_l2_pmu));
+
+	/* Reset all ctrs */
+	set_l2_indirect_reg(L2PMCR, L2PMCR_RESET_ALL);
+
+	/* Avoid spurious interrupt if any */
+	get_reset_pmovsr();
+
+	raw_spin_lock_init(&hw_krait_l2_pmu.lock);
+
+	/* Don't return an arm_pmu here */
+	return NULL;
+}
+#else
+
+static const struct arm_pmu *__init krait_l2_pmu_init(void)
+{
+	return NULL;
+}
+#endif
diff --git a/arch/arm/kernel/perf_event_msm_l2.c b/arch/arm/kernel/perf_event_msm_l2.c
new file mode 100644
index 0000000..26753d8
--- /dev/null
+++ b/arch/arm/kernel/perf_event_msm_l2.c
@@ -0,0 +1,1013 @@
+/*
+ * Copyright (c) 2011, 2012 Code Aurora Forum. 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.
+ */
+#ifdef CONFIG_ARCH_MSM8X60
+
+#include <linux/irq.h>
+
+#define MAX_BB_L2_PERIOD	((1ULL << 32) - 1)
+#define MAX_BB_L2_CTRS 5
+#define BB_L2CYCLE_CTR_BIT 31
+#define BB_L2CYCLE_CTR_EVENT_IDX 4
+#define BB_L2CYCLE_CTR_RAW_CODE 0xfe
+#define SCORPIONL2_PMNC_E       (1 << 0)	/* Enable all counters */
+#define SCORPION_L2_EVT_PREFIX 3
+#define SCORPION_MAX_L2_REG 4
+
+/*
+ * Lock to protect r/m/w sequences to the L2 PMU.
+ */
+DEFINE_RAW_SPINLOCK(bb_l2_pmu_lock);
+
+static struct platform_device *bb_l2_pmu_device;
+
+struct hw_bb_l2_pmu {
+	struct perf_event *events[MAX_BB_L2_CTRS];
+	unsigned long active_mask[BITS_TO_LONGS(MAX_BB_L2_CTRS)];
+	raw_spinlock_t lock;
+};
+
+struct hw_bb_l2_pmu hw_bb_l2_pmu;
+
+struct bb_l2_scorp_evt {
+	u32 evt_type;
+	u32 val;
+	u8 grp;
+	u32 evt_type_act;
+};
+
+enum scorpion_perf_types {
+	SCORPIONL2_TOTAL_BANK_REQ = 0x90,
+	SCORPIONL2_DSIDE_READ = 0x91,
+	SCORPIONL2_DSIDE_WRITE = 0x92,
+	SCORPIONL2_ISIDE_READ = 0x93,
+	SCORPIONL2_L2CACHE_ISIDE_READ = 0x94,
+	SCORPIONL2_L2CACHE_BANK_REQ = 0x95,
+	SCORPIONL2_L2CACHE_DSIDE_READ = 0x96,
+	SCORPIONL2_L2CACHE_DSIDE_WRITE = 0x97,
+	SCORPIONL2_L2NOCACHE_DSIDE_WRITE = 0x98,
+	SCORPIONL2_L2NOCACHE_ISIDE_READ = 0x99,
+	SCORPIONL2_L2NOCACHE_TOTAL_REQ = 0x9a,
+	SCORPIONL2_L2NOCACHE_DSIDE_READ = 0x9b,
+	SCORPIONL2_DSIDE_READ_NOL1 = 0x9c,
+	SCORPIONL2_L2CACHE_WRITETHROUGH = 0x9d,
+	SCORPIONL2_BARRIERS = 0x9e,
+	SCORPIONL2_HARDWARE_TABLE_WALKS = 0x9f,
+	SCORPIONL2_MVA_POC = 0xa0,
+	SCORPIONL2_L2CACHE_HW_TABLE_WALKS = 0xa1,
+	SCORPIONL2_SETWAY_CACHE_OPS = 0xa2,
+	SCORPIONL2_DSIDE_WRITE_HITS = 0xa3,
+	SCORPIONL2_ISIDE_READ_HITS = 0xa4,
+	SCORPIONL2_CACHE_DSIDE_READ_NOL1 = 0xa5,
+	SCORPIONL2_TOTAL_CACHE_HITS = 0xa6,
+	SCORPIONL2_CACHE_MATCH_MISS = 0xa7,
+	SCORPIONL2_DREAD_HIT_L1_DATA = 0xa8,
+	SCORPIONL2_L2LINE_LOCKED = 0xa9,
+	SCORPIONL2_HW_TABLE_WALK_HIT = 0xaa,
+	SCORPIONL2_CACHE_MVA_POC = 0xab,
+	SCORPIONL2_L2ALLOC_DWRITE_MISS = 0xac,
+	SCORPIONL2_CORRECTED_TAG_ARRAY = 0xad,
+	SCORPIONL2_CORRECTED_DATA_ARRAY = 0xae,
+	SCORPIONL2_CORRECTED_REPLACEMENT_ARRAY = 0xaf,
+	SCORPIONL2_PMBUS_MPAAF = 0xb0,
+	SCORPIONL2_PMBUS_MPWDAF = 0xb1,
+	SCORPIONL2_PMBUS_MPBRT = 0xb2,
+	SCORPIONL2_CPU0_GRANT = 0xb3,
+	SCORPIONL2_CPU1_GRANT = 0xb4,
+	SCORPIONL2_CPU0_NOGRANT = 0xb5,
+	SCORPIONL2_CPU1_NOGRANT = 0xb6,
+	SCORPIONL2_CPU0_LOSING_ARB = 0xb7,
+	SCORPIONL2_CPU1_LOSING_ARB = 0xb8,
+	SCORPIONL2_SLAVEPORT_NOGRANT = 0xb9,
+	SCORPIONL2_SLAVEPORT_BPQ_FULL = 0xba,
+	SCORPIONL2_SLAVEPORT_LOSING_ARB = 0xbb,
+	SCORPIONL2_SLAVEPORT_GRANT = 0xbc,
+	SCORPIONL2_SLAVEPORT_GRANTLOCK = 0xbd,
+	SCORPIONL2_L2EM_STREX_PASS = 0xbe,
+	SCORPIONL2_L2EM_STREX_FAIL = 0xbf,
+	SCORPIONL2_LDREX_RESERVE_L2EM = 0xc0,
+	SCORPIONL2_SLAVEPORT_LDREX = 0xc1,
+	SCORPIONL2_CPU0_L2EM_CLEARED = 0xc2,
+	SCORPIONL2_CPU1_L2EM_CLEARED = 0xc3,
+	SCORPIONL2_SLAVEPORT_L2EM_CLEARED = 0xc4,
+	SCORPIONL2_CPU0_CLAMPED = 0xc5,
+	SCORPIONL2_CPU1_CLAMPED = 0xc6,
+	SCORPIONL2_CPU0_WAIT = 0xc7,
+	SCORPIONL2_CPU1_WAIT = 0xc8,
+	SCORPIONL2_CPU0_NONAMBAS_WAIT = 0xc9,
+	SCORPIONL2_CPU1_NONAMBAS_WAIT = 0xca,
+	SCORPIONL2_CPU0_DSB_WAIT = 0xcb,
+	SCORPIONL2_CPU1_DSB_WAIT = 0xcc,
+	SCORPIONL2_AXI_READ = 0xcd,
+	SCORPIONL2_AXI_WRITE = 0xce,
+
+	SCORPIONL2_1BEAT_WRITE = 0xcf,
+	SCORPIONL2_2BEAT_WRITE = 0xd0,
+	SCORPIONL2_4BEAT_WRITE = 0xd1,
+	SCORPIONL2_8BEAT_WRITE = 0xd2,
+	SCORPIONL2_12BEAT_WRITE = 0xd3,
+	SCORPIONL2_16BEAT_WRITE = 0xd4,
+	SCORPIONL2_1BEAT_DSIDE_READ = 0xd5,
+	SCORPIONL2_2BEAT_DSIDE_READ = 0xd6,
+	SCORPIONL2_4BEAT_DSIDE_READ = 0xd7,
+	SCORPIONL2_8BEAT_DSIDE_READ = 0xd8,
+	SCORPIONL2_CSYS_READ_1BEAT = 0xd9,
+	SCORPIONL2_CSYS_READ_2BEAT = 0xda,
+	SCORPIONL2_CSYS_READ_4BEAT = 0xdb,
+	SCORPIONL2_CSYS_READ_8BEAT = 0xdc,
+	SCORPIONL2_4BEAT_IFETCH_READ = 0xdd,
+	SCORPIONL2_8BEAT_IFETCH_READ = 0xde,
+	SCORPIONL2_CSYS_WRITE_1BEAT = 0xdf,
+	SCORPIONL2_CSYS_WRITE_2BEAT = 0xe0,
+	SCORPIONL2_AXI_READ_DATA_BEAT = 0xe1,
+	SCORPIONL2_AXI_WRITE_EVT1 = 0xe2,
+	SCORPIONL2_AXI_WRITE_EVT2 = 0xe3,
+	SCORPIONL2_LDREX_REQ = 0xe4,
+	SCORPIONL2_STREX_PASS = 0xe5,
+	SCORPIONL2_STREX_FAIL = 0xe6,
+	SCORPIONL2_CPREAD = 0xe7,
+	SCORPIONL2_CPWRITE = 0xe8,
+	SCORPIONL2_BARRIER_REQ = 0xe9,
+	SCORPIONL2_AXI_READ_SLVPORT = 0xea,
+	SCORPIONL2_AXI_WRITE_SLVPORT = 0xeb,
+	SCORPIONL2_AXI_READ_SLVPORT_DATABEAT = 0xec,
+	SCORPIONL2_AXI_WRITE_SLVPORT_DATABEAT = 0xed,
+	SCORPIONL2_SNOOPKILL_PREFILTER = 0xee,
+	SCORPIONL2_SNOOPKILL_FILTEROUT = 0xef,
+	SCORPIONL2_SNOOPED_IC = 0xf0,
+	SCORPIONL2_SNOOPED_BP = 0xf1,
+	SCORPIONL2_SNOOPED_BARRIERS = 0xf2,
+	SCORPIONL2_SNOOPED_TLB = 0xf3,
+	BB_L2_MAX_EVT,
+};
+
+static const struct bb_l2_scorp_evt sc_evt[] = {
+	{SCORPIONL2_TOTAL_BANK_REQ, 0x80000001, 0, 0x00},
+	{SCORPIONL2_DSIDE_READ, 0x80000100, 0, 0x01},
+	{SCORPIONL2_DSIDE_WRITE, 0x80010000, 0, 0x02},
+	{SCORPIONL2_ISIDE_READ, 0x81000000, 0, 0x03},
+	{SCORPIONL2_L2CACHE_ISIDE_READ, 0x80000002, 0, 0x00},
+	{SCORPIONL2_L2CACHE_BANK_REQ, 0x80000200, 0, 0x01},
+	{SCORPIONL2_L2CACHE_DSIDE_READ, 0x80020000, 0, 0x02},
+	{SCORPIONL2_L2CACHE_DSIDE_WRITE, 0x82000000, 0, 0x03},
+	{SCORPIONL2_L2NOCACHE_DSIDE_WRITE, 0x80000003, 0, 0x00},
+	{SCORPIONL2_L2NOCACHE_ISIDE_READ, 0x80000300, 0, 0x01},
+	{SCORPIONL2_L2NOCACHE_TOTAL_REQ, 0x80030000, 0, 0x02},
+	{SCORPIONL2_L2NOCACHE_DSIDE_READ, 0x83000000, 0, 0x03},
+	{SCORPIONL2_DSIDE_READ_NOL1, 0x80000004, 0, 0x00},
+	{SCORPIONL2_L2CACHE_WRITETHROUGH, 0x80000400, 0, 0x01},
+	{SCORPIONL2_BARRIERS, 0x84000000, 0, 0x03},
+	{SCORPIONL2_HARDWARE_TABLE_WALKS, 0x80000005, 0, 0x00},
+	{SCORPIONL2_MVA_POC, 0x80000500, 0, 0x01},
+	{SCORPIONL2_L2CACHE_HW_TABLE_WALKS, 0x80050000, 0, 0x02},
+	{SCORPIONL2_SETWAY_CACHE_OPS, 0x85000000, 0, 0x03},
+	{SCORPIONL2_DSIDE_WRITE_HITS, 0x80000006, 0, 0x00},
+	{SCORPIONL2_ISIDE_READ_HITS, 0x80000600, 0, 0x01},
+	{SCORPIONL2_CACHE_DSIDE_READ_NOL1, 0x80060000, 0, 0x02},
+	{SCORPIONL2_TOTAL_CACHE_HITS, 0x86000000, 0, 0x03},
+	{SCORPIONL2_CACHE_MATCH_MISS, 0x80000007, 0, 0x00},
+	{SCORPIONL2_DREAD_HIT_L1_DATA, 0x87000000, 0, 0x03},
+	{SCORPIONL2_L2LINE_LOCKED, 0x80000008, 0, 0x00},
+	{SCORPIONL2_HW_TABLE_WALK_HIT, 0x80000800, 0, 0x01},
+	{SCORPIONL2_CACHE_MVA_POC, 0x80080000, 0, 0x02},
+	{SCORPIONL2_L2ALLOC_DWRITE_MISS, 0x88000000, 0, 0x03},
+	{SCORPIONL2_CORRECTED_TAG_ARRAY, 0x80001A00, 0, 0x01},
+	{SCORPIONL2_CORRECTED_DATA_ARRAY, 0x801A0000, 0, 0x02},
+	{SCORPIONL2_CORRECTED_REPLACEMENT_ARRAY, 0x9A000000, 0, 0x03},
+	{SCORPIONL2_PMBUS_MPAAF, 0x80001C00, 0, 0x01},
+	{SCORPIONL2_PMBUS_MPWDAF, 0x801C0000, 0, 0x02},
+	{SCORPIONL2_PMBUS_MPBRT, 0x9C000000, 0, 0x03},
+
+	{SCORPIONL2_CPU0_GRANT, 0x80000001, 1, 0x04},
+	{SCORPIONL2_CPU1_GRANT, 0x80000100, 1, 0x05},
+	{SCORPIONL2_CPU0_NOGRANT, 0x80020000, 1, 0x06},
+	{SCORPIONL2_CPU1_NOGRANT, 0x82000000, 1, 0x07},
+	{SCORPIONL2_CPU0_LOSING_ARB, 0x80040000, 1, 0x06},
+	{SCORPIONL2_CPU1_LOSING_ARB, 0x84000000, 1, 0x07},
+	{SCORPIONL2_SLAVEPORT_NOGRANT, 0x80000007, 1, 0x04},
+	{SCORPIONL2_SLAVEPORT_BPQ_FULL, 0x80000700, 1, 0x05},
+	{SCORPIONL2_SLAVEPORT_LOSING_ARB, 0x80070000, 1, 0x06},
+	{SCORPIONL2_SLAVEPORT_GRANT, 0x87000000, 1, 0x07},
+	{SCORPIONL2_SLAVEPORT_GRANTLOCK, 0x80000008, 1, 0x04},
+	{SCORPIONL2_L2EM_STREX_PASS, 0x80000009, 1, 0x04},
+	{SCORPIONL2_L2EM_STREX_FAIL, 0x80000900, 1, 0x05},
+	{SCORPIONL2_LDREX_RESERVE_L2EM, 0x80090000, 1, 0x06},
+	{SCORPIONL2_SLAVEPORT_LDREX, 0x89000000, 1, 0x07},
+	{SCORPIONL2_CPU0_L2EM_CLEARED, 0x800A0000, 1, 0x06},
+	{SCORPIONL2_CPU1_L2EM_CLEARED, 0x8A000000, 1, 0x07},
+	{SCORPIONL2_SLAVEPORT_L2EM_CLEARED, 0x80000B00, 1, 0x05},
+	{SCORPIONL2_CPU0_CLAMPED, 0x8000000E, 1, 0x04},
+	{SCORPIONL2_CPU1_CLAMPED, 0x80000E00, 1, 0x05},
+	{SCORPIONL2_CPU0_WAIT, 0x800F0000, 1, 0x06},
+	{SCORPIONL2_CPU1_WAIT, 0x8F000000, 1, 0x07},
+	{SCORPIONL2_CPU0_NONAMBAS_WAIT, 0x80000010, 1, 0x04},
+	{SCORPIONL2_CPU1_NONAMBAS_WAIT, 0x80001000, 1, 0x05},
+	{SCORPIONL2_CPU0_DSB_WAIT, 0x80000014, 1, 0x04},
+	{SCORPIONL2_CPU1_DSB_WAIT, 0x80001400, 1, 0x05},
+
+	{SCORPIONL2_AXI_READ, 0x80000001, 2, 0x08},
+	{SCORPIONL2_AXI_WRITE, 0x80000100, 2, 0x09},
+	{SCORPIONL2_1BEAT_WRITE, 0x80010000, 2, 0x0a},
+	{SCORPIONL2_2BEAT_WRITE, 0x80010000, 2, 0x0b},
+	{SCORPIONL2_4BEAT_WRITE, 0x80000002, 2, 0x08},
+	{SCORPIONL2_8BEAT_WRITE, 0x80000200, 2, 0x09},
+	{SCORPIONL2_12BEAT_WRITE, 0x80020000, 2, 0x0a},
+	{SCORPIONL2_16BEAT_WRITE, 0x82000000, 2, 0x0b},
+	{SCORPIONL2_1BEAT_DSIDE_READ, 0x80000003, 2, 0x08},
+	{SCORPIONL2_2BEAT_DSIDE_READ, 0x80000300, 2, 0x09},
+	{SCORPIONL2_4BEAT_DSIDE_READ, 0x80030000, 2, 0x0a},
+	{SCORPIONL2_8BEAT_DSIDE_READ, 0x83000000, 2, 0x0b},
+	{SCORPIONL2_CSYS_READ_1BEAT, 0x80000004, 2, 0x08},
+	{SCORPIONL2_CSYS_READ_2BEAT, 0x80000400, 2, 0x09},
+	{SCORPIONL2_CSYS_READ_4BEAT, 0x80040000, 2, 0x0a},
+	{SCORPIONL2_CSYS_READ_8BEAT, 0x84000000, 2, 0x0b},
+	{SCORPIONL2_4BEAT_IFETCH_READ, 0x80000005, 2, 0x08},
+	{SCORPIONL2_8BEAT_IFETCH_READ, 0x80000500, 2, 0x09},
+	{SCORPIONL2_CSYS_WRITE_1BEAT, 0x80050000, 2, 0x0a},
+	{SCORPIONL2_CSYS_WRITE_2BEAT, 0x85000000, 2, 0x0b},
+	{SCORPIONL2_AXI_READ_DATA_BEAT, 0x80000600, 2, 0x09},
+	{SCORPIONL2_AXI_WRITE_EVT1, 0x80060000, 2, 0x0a},
+	{SCORPIONL2_AXI_WRITE_EVT2, 0x86000000, 2, 0x0b},
+	{SCORPIONL2_LDREX_REQ, 0x80000007, 2, 0x08},
+	{SCORPIONL2_STREX_PASS, 0x80000700, 2, 0x09},
+	{SCORPIONL2_STREX_FAIL, 0x80070000, 2, 0x0a},
+	{SCORPIONL2_CPREAD, 0x80000008, 2, 0x08},
+	{SCORPIONL2_CPWRITE, 0x80000800, 2, 0x09},
+	{SCORPIONL2_BARRIER_REQ, 0x88000000, 2, 0x0b},
+
+	{SCORPIONL2_AXI_READ_SLVPORT, 0x80000001, 3, 0x0c},
+	{SCORPIONL2_AXI_WRITE_SLVPORT, 0x80000100, 3, 0x0d},
+	{SCORPIONL2_AXI_READ_SLVPORT_DATABEAT, 0x80010000, 3, 0x0e},
+	{SCORPIONL2_AXI_WRITE_SLVPORT_DATABEAT, 0x81000000, 3, 0x0f},
+
+	{SCORPIONL2_SNOOPKILL_PREFILTER, 0x80000001, 4, 0x10},
+	{SCORPIONL2_SNOOPKILL_FILTEROUT, 0x80000100, 4, 0x11},
+	{SCORPIONL2_SNOOPED_IC, 0x80000002, 4, 0x10},
+	{SCORPIONL2_SNOOPED_BP, 0x80000200, 4, 0x11},
+	{SCORPIONL2_SNOOPED_BARRIERS, 0x80020000, 4, 0x12},
+	{SCORPIONL2_SNOOPED_TLB, 0x82000000, 4, 0x13},
+};
+
+static u32 bb_l2_read_l2pm0(void)
+{
+	u32 val;
+	asm volatile ("mrc p15, 3, %0, c15, c7, 0" : "=r" (val));
+	return val;
+}
+
+static void bb_l2_write_l2pm0(u32 val)
+{
+	asm volatile ("mcr p15, 3, %0, c15, c7, 0" : : "r" (val));
+}
+
+static u32 bb_l2_read_l2pm1(void)
+{
+	u32 val;
+	asm volatile ("mrc p15, 3, %0, c15, c7, 1" : "=r" (val));
+	return val;
+}
+
+static void bb_l2_write_l2pm1(u32 val)
+{
+	asm volatile ("mcr p15, 3, %0, c15, c7, 1" : : "r" (val));
+}
+
+static u32 bb_l2_read_l2pm2(void)
+{
+	u32 val;
+	asm volatile ("mrc p15, 3, %0, c15, c7, 2" : "=r" (val));
+	return val;
+}
+
+static void bb_l2_write_l2pm2(u32 val)
+{
+	asm volatile ("mcr p15, 3, %0, c15, c7, 2" : : "r" (val));
+}
+
+static u32 bb_l2_read_l2pm3(void)
+{
+	u32 val;
+	asm volatile ("mrc p15, 3, %0, c15, c7, 3" : "=r" (val));
+	return val;
+}
+
+static void bb_l2_write_l2pm3(u32 val)
+{
+	asm volatile ("mcr p15, 3, %0, c15, c7, 3" : : "r" (val));
+}
+
+static u32 bb_l2_read_l2pm4(void)
+{
+	u32 val;
+	asm volatile ("mrc p15, 3, %0, c15, c7, 4" : "=r" (val));
+	return val;
+}
+
+static void bb_l2_write_l2pm4(u32 val)
+{
+	asm volatile ("mcr p15, 3, %0, c15, c7, 4" : : "r" (val));
+}
+
+struct bb_scorpion_access_funcs {
+	u32(*read) (void);
+	void (*write) (u32);
+	void (*pre) (void);
+	void (*post) (void);
+};
+
+struct bb_scorpion_access_funcs bb_l2_func[] = {
+	{bb_l2_read_l2pm0, bb_l2_write_l2pm0, NULL, NULL},
+	{bb_l2_read_l2pm1, bb_l2_write_l2pm1, NULL, NULL},
+	{bb_l2_read_l2pm2, bb_l2_write_l2pm2, NULL, NULL},
+	{bb_l2_read_l2pm3, bb_l2_write_l2pm3, NULL, NULL},
+	{bb_l2_read_l2pm4, bb_l2_write_l2pm4, NULL, NULL},
+};
+
+#define COLMN0MASK 0x000000ff
+#define COLMN1MASK 0x0000ff00
+#define COLMN2MASK 0x00ff0000
+
+static u32 bb_l2_get_columnmask(u32 setval)
+{
+	if (setval & COLMN0MASK)
+		return 0xffffff00;
+	else if (setval & COLMN1MASK)
+		return 0xffff00ff;
+	else if (setval & COLMN2MASK)
+		return 0xff00ffff;
+	else
+		return 0x80ffffff;
+}
+
+static void bb_l2_evt_setup(u32 gr, u32 setval)
+{
+	u32 val;
+	if (bb_l2_func[gr].pre)
+		bb_l2_func[gr].pre();
+	val = bb_l2_get_columnmask(setval) & bb_l2_func[gr].read();
+	val = val | setval;
+	bb_l2_func[gr].write(val);
+	if (bb_l2_func[gr].post)
+		bb_l2_func[gr].post();
+}
+
+#define BB_L2_EVT_START_IDX 0x90
+#define BB_L2_INV_EVTYPE 0
+
+static unsigned int get_bb_l2_evtinfo(unsigned int evt_type,
+				      struct bb_l2_scorp_evt *evtinfo)
+{
+	u32 idx;
+	u8 prefix;
+	u8 reg;
+	u8 code;
+	u8 group;
+
+	prefix = (evt_type & 0xF0000) >> 16;
+	if (prefix == SCORPION_L2_EVT_PREFIX) {
+		reg   = (evt_type & 0x0F000) >> 12;
+		code  = (evt_type & 0x00FF0) >> 4;
+		group =  evt_type & 0x0000F;
+
+		if ((group > 3) || (reg > SCORPION_MAX_L2_REG))
+			return BB_L2_INV_EVTYPE;
+
+		evtinfo->val = 0x80000000 | (code << (group * 8));
+		evtinfo->grp = reg;
+		evtinfo->evt_type_act = group | (reg << 2);
+		return evtinfo->evt_type_act;
+	}
+
+	if (evt_type < BB_L2_EVT_START_IDX || evt_type >= BB_L2_MAX_EVT)
+		return BB_L2_INV_EVTYPE;
+	idx = evt_type - BB_L2_EVT_START_IDX;
+	if (sc_evt[idx].evt_type == evt_type) {
+		evtinfo->val = sc_evt[idx].val;
+		evtinfo->grp = sc_evt[idx].grp;
+		evtinfo->evt_type_act = sc_evt[idx].evt_type_act;
+		return sc_evt[idx].evt_type_act;
+	}
+	return BB_L2_INV_EVTYPE;
+}
+
+static inline void bb_l2_pmnc_write(unsigned long val)
+{
+	val &= 0xff;
+	asm volatile ("mcr p15, 3, %0, c15, c4, 0" : : "r" (val));
+}
+
+static inline unsigned long bb_l2_pmnc_read(void)
+{
+	u32 val;
+	asm volatile ("mrc p15, 3, %0, c15, c4, 0" : "=r" (val));
+	return val;
+}
+
+static void bb_l2_set_evcntcr(void)
+{
+	u32 val = 0x0;
+	asm volatile ("mcr p15, 3, %0, c15, c6, 4" : : "r" (val));
+}
+
+static inline void bb_l2_set_evtyper(int ctr, int val)
+{
+	/* select ctr */
+	asm volatile ("mcr p15, 3, %0, c15, c6, 0" : : "r" (ctr));
+
+	/* write into EVTYPER */
+	asm volatile ("mcr p15, 3, %0, c15, c6, 7" : : "r" (val));
+}
+
+static void bb_l2_set_evfilter_task_mode(void)
+{
+	u32 filter_val = 0x000f0030 | 1 << smp_processor_id();
+
+	asm volatile ("mcr p15, 3, %0, c15, c6, 3" : : "r" (filter_val));
+}
+
+static void bb_l2_set_evfilter_sys_mode(void)
+{
+	u32 filter_val = 0x000f003f;
+
+	asm volatile ("mcr p15, 3, %0, c15, c6, 3" : : "r" (filter_val));
+}
+
+static void bb_l2_enable_intenset(u32 idx)
+{
+	if (idx == BB_L2CYCLE_CTR_EVENT_IDX) {
+		asm volatile ("mcr p15, 3, %0, c15, c5, 1" : : "r"
+			      (1 << BB_L2CYCLE_CTR_BIT));
+	} else {
+		asm volatile ("mcr p15, 3, %0, c15, c5, 1" : : "r" (1 << idx));
+	}
+}
+
+static void bb_l2_disable_intenclr(u32 idx)
+{
+	if (idx == BB_L2CYCLE_CTR_EVENT_IDX) {
+		asm volatile ("mcr p15, 3, %0, c15, c5, 0" : : "r"
+			      (1 << BB_L2CYCLE_CTR_BIT));
+	} else {
+		asm volatile ("mcr p15, 3, %0, c15, c5, 0" : : "r" (1 << idx));
+	}
+}
+
+static void bb_l2_enable_counter(u32 idx)
+{
+	if (idx == BB_L2CYCLE_CTR_EVENT_IDX) {
+		asm volatile ("mcr p15, 3, %0, c15, c4, 3" : : "r"
+			      (1 << BB_L2CYCLE_CTR_BIT));
+	} else {
+		asm volatile ("mcr p15, 3, %0, c15, c4, 3" : : "r" (1 << idx));
+	}
+}
+
+static void bb_l2_disable_counter(u32 idx)
+{
+	if (idx == BB_L2CYCLE_CTR_EVENT_IDX) {
+		asm volatile ("mcr p15, 3, %0, c15, c4, 2" : : "r"
+			      (1 << BB_L2CYCLE_CTR_BIT));
+
+	} else {
+		asm volatile ("mcr p15, 3, %0, c15, c4, 2" : : "r" (1 << idx));
+	}
+}
+
+static u64 bb_l2_read_counter(u32 idx)
+{
+	u32 val;
+	unsigned long flags;
+
+	if (idx == BB_L2CYCLE_CTR_EVENT_IDX) {
+		asm volatile ("mrc p15, 3, %0, c15, c4, 5" : "=r" (val));
+	} else {
+		raw_spin_lock_irqsave(&bb_l2_pmu_lock, flags);
+		asm volatile ("mcr p15, 3, %0, c15, c6, 0" : : "r" (idx));
+
+		/* read val from counter */
+		asm volatile ("mrc p15, 3, %0, c15, c6, 5" : "=r" (val));
+		raw_spin_unlock_irqrestore(&bb_l2_pmu_lock, flags);
+	}
+
+	return val;
+}
+
+static void bb_l2_write_counter(u32 idx, u32 val)
+{
+	unsigned long flags;
+
+	if (idx == BB_L2CYCLE_CTR_EVENT_IDX) {
+		asm volatile ("mcr p15, 3, %0, c15, c4, 5" : : "r" (val));
+	} else {
+		raw_spin_lock_irqsave(&bb_l2_pmu_lock, flags);
+		/* select counter */
+		asm volatile ("mcr p15, 3, %0, c15, c6, 0" : : "r" (idx));
+
+		/* write val into counter */
+		asm volatile ("mcr p15, 3, %0, c15, c6, 5" : : "r" (val));
+		raw_spin_unlock_irqrestore(&bb_l2_pmu_lock, flags);
+	}
+}
+
+static int
+bb_pmu_event_set_period(struct perf_event *event,
+			struct hw_perf_event *hwc, int idx)
+{
+	s64 left = local64_read(&hwc->period_left);
+	s64 period = hwc->sample_period;
+	int ret = 0;
+
+	if (unlikely(left <= -period)) {
+		left = period;
+		local64_set(&hwc->period_left, left);
+		hwc->last_period = period;
+		ret = 1;
+	}
+
+	if (unlikely(left <= 0)) {
+		left += period;
+		local64_set(&hwc->period_left, left);
+		hwc->last_period = period;
+		ret = 1;
+	}
+
+	if (left > (s64) MAX_BB_L2_PERIOD)
+		left = MAX_BB_L2_PERIOD;
+
+	local64_set(&hwc->prev_count, (u64)-left);
+
+	bb_l2_write_counter(idx, (u64) (-left) & 0xffffffff);
+
+	perf_event_update_userpage(event);
+
+	return ret;
+}
+
+static u64
+bb_pmu_event_update(struct perf_event *event, struct hw_perf_event *hwc,
+		    int idx, int overflow)
+{
+	u64 prev_raw_count, new_raw_count;
+	u64 delta;
+
+again:
+	prev_raw_count = local64_read(&hwc->prev_count);
+	new_raw_count = bb_l2_read_counter(idx);
+
+	if (local64_cmpxchg(&hwc->prev_count, prev_raw_count,
+			    new_raw_count) != prev_raw_count)
+		goto again;
+
+	new_raw_count &= MAX_BB_L2_PERIOD;
+	prev_raw_count &= MAX_BB_L2_PERIOD;
+
+	if (overflow) {
+		delta = MAX_BB_L2_PERIOD - prev_raw_count + new_raw_count;
+		pr_err("%s: delta: %lld\n", __func__, delta);
+	} else
+		delta = new_raw_count - prev_raw_count;
+
+	local64_add(delta, &event->count);
+	local64_sub(delta, &hwc->period_left);
+
+	pr_debug("%s: new: %lld, prev: %lld, event: %ld count: %lld\n",
+		 __func__, new_raw_count, prev_raw_count,
+		 hwc->config_base, local64_read(&event->count));
+
+	return new_raw_count;
+}
+
+static void bb_l2_read(struct perf_event *event)
+{
+	struct hw_perf_event *hwc = &event->hw;
+
+	bb_pmu_event_update(event, hwc, hwc->idx, 0);
+}
+
+static void bb_l2_stop_counter(struct perf_event *event, int flags)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	int idx = hwc->idx;
+
+	if (!(hwc->state & PERF_HES_STOPPED)) {
+		bb_l2_disable_intenclr(idx);
+		bb_l2_disable_counter(idx);
+
+		bb_pmu_event_update(event, hwc, idx, 0);
+		hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE;
+	}
+
+	pr_debug("%s: event: %ld ctr: %d stopped\n", __func__, hwc->config_base,
+		 idx);
+}
+
+static void bb_l2_start_counter(struct perf_event *event, int flags)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	int idx = hwc->idx;
+	struct bb_l2_scorp_evt evtinfo;
+	int evtype = hwc->config_base;
+	int ev_typer;
+	unsigned long iflags;
+	int cpu_id = smp_processor_id();
+
+	if (flags & PERF_EF_RELOAD)
+		WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE));
+
+	hwc->state = 0;
+
+	bb_pmu_event_set_period(event, hwc, idx);
+
+	if (hwc->config_base == BB_L2CYCLE_CTR_RAW_CODE)
+		goto out;
+
+	memset(&evtinfo, 0, sizeof(evtinfo));
+
+	ev_typer = get_bb_l2_evtinfo(evtype, &evtinfo);
+
+	raw_spin_lock_irqsave(&bb_l2_pmu_lock, iflags);
+
+	bb_l2_set_evtyper(idx, ev_typer);
+
+	bb_l2_set_evcntcr();
+
+	if (event->cpu < 0)
+		bb_l2_set_evfilter_task_mode();
+	else
+		bb_l2_set_evfilter_sys_mode();
+
+	bb_l2_evt_setup(evtinfo.grp, evtinfo.val);
+
+	raw_spin_unlock_irqrestore(&bb_l2_pmu_lock, iflags);
+
+out:
+
+	bb_l2_enable_intenset(idx);
+
+	bb_l2_enable_counter(idx);
+
+	pr_debug("%s: idx: %d, event: %d, val: %x, cpu: %d\n",
+		 __func__, idx, evtype, evtinfo.val, cpu_id);
+}
+
+static void bb_l2_del_event(struct perf_event *event, int flags)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	int idx = hwc->idx;
+	unsigned long iflags;
+
+	raw_spin_lock_irqsave(&hw_bb_l2_pmu.lock, iflags);
+
+	clear_bit(idx, (long unsigned int *)(&hw_bb_l2_pmu.active_mask));
+
+	bb_l2_stop_counter(event, PERF_EF_UPDATE);
+	hw_bb_l2_pmu.events[idx] = NULL;
+	hwc->idx = -1;
+
+	raw_spin_unlock_irqrestore(&hw_bb_l2_pmu.lock, iflags);
+
+	pr_debug("%s: event: %ld deleted\n", __func__, hwc->config_base);
+
+	perf_event_update_userpage(event);
+}
+
+static int bb_l2_add_event(struct perf_event *event, int flags)
+{
+	int ctr = 0;
+	struct hw_perf_event *hwc = &event->hw;
+	unsigned long iflags;
+	int err = 0;
+
+	perf_pmu_disable(event->pmu);
+
+	raw_spin_lock_irqsave(&hw_bb_l2_pmu.lock, iflags);
+
+	/* Cycle counter has a resrvd index */
+	if (hwc->config_base == BB_L2CYCLE_CTR_RAW_CODE) {
+		if (hw_bb_l2_pmu.events[BB_L2CYCLE_CTR_EVENT_IDX]) {
+			pr_err("%s: Stale cycle ctr event ptr !\n", __func__);
+			err = -EINVAL;
+			goto out;
+		}
+		hwc->idx = BB_L2CYCLE_CTR_EVENT_IDX;
+		hw_bb_l2_pmu.events[BB_L2CYCLE_CTR_EVENT_IDX] = event;
+		set_bit(BB_L2CYCLE_CTR_EVENT_IDX,
+			(long unsigned int *)&hw_bb_l2_pmu.active_mask);
+		goto skip_ctr_loop;
+	}
+
+	for (ctr = 0; ctr < MAX_BB_L2_CTRS - 1; ctr++) {
+		if (!hw_bb_l2_pmu.events[ctr]) {
+			hwc->idx = ctr;
+			hw_bb_l2_pmu.events[ctr] = event;
+			set_bit(ctr, (long unsigned int *)
+				&hw_bb_l2_pmu.active_mask);
+			break;
+		}
+	}
+
+	if (hwc->idx < 0) {
+		err = -ENOSPC;
+		pr_err("%s: No space for event: %llx!!\n", __func__,
+		       event->attr.config);
+		goto out;
+	}
+
+skip_ctr_loop:
+
+	bb_l2_disable_counter(hwc->idx);
+
+	hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE;
+
+	if (flags & PERF_EF_START)
+		bb_l2_start_counter(event, PERF_EF_RELOAD);
+
+	perf_event_update_userpage(event);
+
+	pr_debug("%s: event: %ld, ctr: %d added from cpu:%d\n",
+		 __func__, hwc->config_base, hwc->idx, smp_processor_id());
+out:
+	raw_spin_unlock_irqrestore(&hw_bb_l2_pmu.lock, iflags);
+
+	/* Resume the PMU even if this event could not be added */
+	perf_pmu_enable(event->pmu);
+
+	return err;
+}
+
+static void bb_l2_pmu_enable(struct pmu *pmu)
+{
+	unsigned long flags;
+	isb();
+	raw_spin_lock_irqsave(&bb_l2_pmu_lock, flags);
+	/* Enable all counters */
+	bb_l2_pmnc_write(bb_l2_pmnc_read() | SCORPIONL2_PMNC_E);
+	raw_spin_unlock_irqrestore(&bb_l2_pmu_lock, flags);
+}
+
+static void bb_l2_pmu_disable(struct pmu *pmu)
+{
+	unsigned long flags;
+	raw_spin_lock_irqsave(&bb_l2_pmu_lock, flags);
+	/* Disable all counters */
+	bb_l2_pmnc_write(bb_l2_pmnc_read() & ~SCORPIONL2_PMNC_E);
+	raw_spin_unlock_irqrestore(&bb_l2_pmu_lock, flags);
+	isb();
+}
+
+static inline u32 bb_l2_get_reset_pmovsr(void)
+{
+	u32 val;
+
+	/* Read */
+	asm volatile ("mrc p15, 3, %0, c15, c4, 1" : "=r" (val));
+
+	/* Write to clear flags */
+	val &= 0xffffffff;
+	asm volatile ("mcr p15, 3, %0, c15, c4, 1" : : "r" (val));
+
+	return val;
+}
+
+static irqreturn_t bb_l2_handle_irq(int irq_num, void *dev)
+{
+	unsigned long pmovsr;
+	struct perf_sample_data data;
+	struct pt_regs *regs;
+	struct perf_event *event;
+	struct hw_perf_event *hwc;
+	int bitp;
+	int idx = 0;
+
+	pmovsr = bb_l2_get_reset_pmovsr();
+
+	if (!(pmovsr & 0xffffffff))
+		return IRQ_NONE;
+
+	regs = get_irq_regs();
+
+	perf_sample_data_init(&data, 0);
+
+	raw_spin_lock(&hw_bb_l2_pmu.lock);
+
+	while (pmovsr) {
+		bitp = __ffs(pmovsr);
+
+		if (bitp == BB_L2CYCLE_CTR_BIT)
+			idx = BB_L2CYCLE_CTR_EVENT_IDX;
+		else
+			idx = bitp;
+
+		event = hw_bb_l2_pmu.events[idx];
+
+		if (!event)
+			goto next;
+
+		if (!test_bit(idx, hw_bb_l2_pmu.active_mask))
+			goto next;
+
+		hwc = &event->hw;
+		bb_pmu_event_update(event, hwc, idx, 1);
+		data.period = event->hw.last_period;
+
+		if (!bb_pmu_event_set_period(event, hwc, idx))
+			goto next;
+
+		if (perf_event_overflow(event, 0, &data, regs))
+			bb_l2_disable_counter(hwc->idx);
+next:
+		pmovsr &= (pmovsr - 1);
+	}
+
+	raw_spin_unlock(&hw_bb_l2_pmu.lock);
+
+	irq_work_run();
+
+	return IRQ_HANDLED;
+}
+
+static atomic_t active_bb_l2_events = ATOMIC_INIT(0);
+static DEFINE_MUTEX(bb_pmu_reserve_mutex);
+
+static int bb_pmu_reserve_hardware(void)
+{
+	int i, err = -ENODEV, irq;
+
+	bb_l2_pmu_device = reserve_pmu(ARM_PMU_DEVICE_L2);
+
+	if (IS_ERR(bb_l2_pmu_device)) {
+		pr_warning("unable to reserve pmu\n");
+		return PTR_ERR(bb_l2_pmu_device);
+	}
+
+	if (bb_l2_pmu_device->num_resources < 1) {
+		pr_err("no irqs for PMUs defined\n");
+		return -ENODEV;
+	}
+
+	if (strncmp(bb_l2_pmu_device->name, "l2-arm-pmu", 6)) {
+		pr_err("Incorrect pdev reserved !\n");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < bb_l2_pmu_device->num_resources; ++i) {
+		irq = platform_get_irq(bb_l2_pmu_device, i);
+		if (irq < 0)
+			continue;
+
+		err = request_irq(irq, bb_l2_handle_irq,
+				  IRQF_DISABLED | IRQF_NOBALANCING,
+				  "bb-l2-pmu", NULL);
+		if (err) {
+			pr_warning("unable to request IRQ%d for Krait L2 perf "
+				   "counters\n", irq);
+			break;
+		}
+
+		irq_get_chip(irq)->irq_unmask(irq_get_irq_data(irq));
+	}
+
+	if (err) {
+		for (i = i - 1; i >= 0; --i) {
+			irq = platform_get_irq(bb_l2_pmu_device, i);
+			if (irq >= 0)
+				free_irq(irq, NULL);
+		}
+		release_pmu(bb_l2_pmu_device);
+		bb_l2_pmu_device = NULL;
+	}
+
+	return err;
+}
+
+static void bb_pmu_release_hardware(void)
+{
+	int i, irq;
+
+	for (i = bb_l2_pmu_device->num_resources - 1; i >= 0; --i) {
+		irq = platform_get_irq(bb_l2_pmu_device, i);
+		if (irq >= 0)
+			free_irq(irq, NULL);
+	}
+
+	bb_l2_pmu_disable(NULL);
+
+	release_pmu(bb_l2_pmu_device);
+	bb_l2_pmu_device = NULL;
+}
+
+static void bb_pmu_perf_event_destroy(struct perf_event *event)
+{
+	if (atomic_dec_and_mutex_lock
+	    (&active_bb_l2_events, &bb_pmu_reserve_mutex)) {
+		bb_pmu_release_hardware();
+		mutex_unlock(&bb_pmu_reserve_mutex);
+	}
+}
+
+static int bb_l2_event_init(struct perf_event *event)
+{
+	int err = 0;
+	struct hw_perf_event *hwc = &event->hw;
+	int status = 0;
+
+	switch (event->attr.type) {
+	case PERF_TYPE_SHARED:
+		break;
+
+	default:
+		return -ENOENT;
+	}
+
+	hwc->idx = -1;
+
+	event->destroy = bb_pmu_perf_event_destroy;
+
+	if (!atomic_inc_not_zero(&active_bb_l2_events)) {
+		/* 0 active events */
+		mutex_lock(&bb_pmu_reserve_mutex);
+		err = bb_pmu_reserve_hardware();
+		mutex_unlock(&bb_pmu_reserve_mutex);
+		if (!err)
+			atomic_inc(&active_bb_l2_events);
+		else
+			return err;
+	}
+
+	hwc->config = 0;
+	hwc->event_base = 0;
+
+	/* Check if we came via perf default syms */
+	if (event->attr.config == PERF_COUNT_HW_L2_CYCLES)
+		hwc->config_base = BB_L2CYCLE_CTR_RAW_CODE;
+	else
+		hwc->config_base = event->attr.config;
+
+	/* Only one CPU can control the cycle counter */
+	if (hwc->config_base == BB_L2CYCLE_CTR_RAW_CODE) {
+		/* Check if its already running */
+		asm volatile ("mrc p15, 3, %0, c15, c4, 6" : "=r" (status));
+		if (status == 0x2) {
+			err = -ENOSPC;
+			goto out;
+		}
+	}
+
+	if (!hwc->sample_period) {
+		hwc->sample_period = MAX_BB_L2_PERIOD;
+		hwc->last_period = hwc->sample_period;
+		local64_set(&hwc->period_left, hwc->sample_period);
+	}
+
+	pr_debug("%s: event: %lld init'd\n", __func__, event->attr.config);
+
+out:
+	if (err < 0)
+		bb_pmu_perf_event_destroy(event);
+
+	return err;
+}
+
+static struct pmu bb_l2_pmu = {
+	.pmu_enable = bb_l2_pmu_enable,
+	.pmu_disable = bb_l2_pmu_disable,
+	.event_init = bb_l2_event_init,
+	.add = bb_l2_add_event,
+	.del = bb_l2_del_event,
+	.start = bb_l2_start_counter,
+	.stop = bb_l2_stop_counter,
+	.read = bb_l2_read,
+};
+
+static const struct arm_pmu *__init scorpionmp_l2_pmu_init(void)
+{
+	/* Register our own PMU here */
+	perf_pmu_register(&bb_l2_pmu, "BB L2", PERF_TYPE_SHARED);
+
+	memset(&hw_bb_l2_pmu, 0, sizeof(hw_bb_l2_pmu));
+
+	/* Avoid spurious interrupts at startup */
+	bb_l2_get_reset_pmovsr();
+
+	raw_spin_lock_init(&hw_bb_l2_pmu.lock);
+
+	/* Don't return an arm_pmu here */
+	return NULL;
+}
+#else
+
+static const struct arm_pmu *__init scorpionmp_l2_pmu_init(void)
+{
+	return NULL;
+}
+
+#endif
diff --git a/arch/arm/kernel/perf_event_v6.c b/arch/arm/kernel/perf_event_v6.c
index b78af0c..b5ba783 100644
--- a/arch/arm/kernel/perf_event_v6.c
+++ b/arch/arm/kernel/perf_event_v6.c
@@ -76,7 +76,7 @@
 	[PERF_COUNT_HW_STALLED_CYCLES_BACKEND]	= ARMV6_PERFCTR_LSU_FULL_STALL,
 };
 
-static const unsigned armv6_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
+static unsigned armv6_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
 					  [PERF_COUNT_HW_CACHE_OP_MAX]
 					  [PERF_COUNT_HW_CACHE_RESULT_MAX] = {
 	[C(L1D)] = {
@@ -231,7 +231,7 @@
 	[PERF_COUNT_HW_STALLED_CYCLES_BACKEND]	= ARMV6MPCORE_PERFCTR_LSU_FULL_STALL,
 };
 
-static const unsigned armv6mpcore_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
+static unsigned armv6mpcore_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
 					[PERF_COUNT_HW_CACHE_OP_MAX]
 					[PERF_COUNT_HW_CACHE_RESULT_MAX] = {
 	[C(L1D)] = {
@@ -655,6 +655,8 @@
 	.id			= ARM_PERF_PMU_ID_V6,
 	.name			= "v6",
 	.handle_irq		= armv6pmu_handle_irq,
+	.request_pmu_irq	= armpmu_generic_request_irq,
+	.free_pmu_irq		= armpmu_generic_free_irq,
 	.enable			= armv6pmu_enable_event,
 	.disable		= armv6pmu_disable_event,
 	.read_counter		= armv6pmu_read_counter,
@@ -690,6 +692,8 @@
 	.id			= ARM_PERF_PMU_ID_V6MP,
 	.name			= "v6mpcore",
 	.handle_irq		= armv6pmu_handle_irq,
+	.request_pmu_irq	= armpmu_generic_request_irq,
+	.free_pmu_irq		= armpmu_generic_free_irq,
 	.enable			= armv6pmu_enable_event,
 	.disable		= armv6mpcore_pmu_disable_event,
 	.read_counter		= armv6pmu_read_counter,
diff --git a/arch/arm/kernel/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c
index 00755d8..678c55d 100644
--- a/arch/arm/kernel/perf_event_v7.c
+++ b/arch/arm/kernel/perf_event_v7.c
@@ -130,7 +130,7 @@
 	[PERF_COUNT_HW_STALLED_CYCLES_BACKEND]	= HW_OP_UNSUPPORTED,
 };
 
-static const unsigned armv7_a8_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
+static unsigned armv7_a8_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
 					  [PERF_COUNT_HW_CACHE_OP_MAX]
 					  [PERF_COUNT_HW_CACHE_RESULT_MAX] = {
 	[C(L1D)] = {
@@ -254,7 +254,7 @@
 	[PERF_COUNT_HW_STALLED_CYCLES_BACKEND]	= ARMV7_A9_PERFCTR_STALL_DISPATCH,
 };
 
-static const unsigned armv7_a9_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
+static unsigned armv7_a9_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
 					  [PERF_COUNT_HW_CACHE_OP_MAX]
 					  [PERF_COUNT_HW_CACHE_RESULT_MAX] = {
 	[C(L1D)] = {
@@ -378,7 +378,7 @@
 	[PERF_COUNT_HW_STALLED_CYCLES_BACKEND]	= HW_OP_UNSUPPORTED,
 };
 
-static const unsigned armv7_a5_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
+static unsigned armv7_a5_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
 					[PERF_COUNT_HW_CACHE_OP_MAX]
 					[PERF_COUNT_HW_CACHE_RESULT_MAX] = {
 	[C(L1D)] = {
@@ -500,7 +500,7 @@
 	[PERF_COUNT_HW_STALLED_CYCLES_BACKEND]	= HW_OP_UNSUPPORTED,
 };
 
-static const unsigned armv7_a15_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
+static unsigned armv7_a15_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
 					[PERF_COUNT_HW_CACHE_OP_MAX]
 					[PERF_COUNT_HW_CACHE_RESULT_MAX] = {
 	[C(L1D)] = {
@@ -624,7 +624,7 @@
 	[PERF_COUNT_HW_STALLED_CYCLES_BACKEND]	= HW_OP_UNSUPPORTED,
 };
 
-static const unsigned armv7_a7_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
+static unsigned armv7_a7_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
 					[PERF_COUNT_HW_CACHE_OP_MAX]
 					[PERF_COUNT_HW_CACHE_RESULT_MAX] = {
 	[C(L1D)] = {
@@ -993,7 +993,7 @@
 }
 #endif
 
-static void armv7pmu_enable_event(struct hw_perf_event *hwc, int idx)
+static void armv7pmu_enable_event(struct hw_perf_event *hwc, int idx, int cpu)
 {
 	unsigned long flags;
 	struct pmu_hw_events *events = cpu_pmu->get_hw_events();
diff --git a/arch/arm/kernel/perf_event_xscale.c b/arch/arm/kernel/perf_event_xscale.c
index 71a21e6..1a751f7 100644
--- a/arch/arm/kernel/perf_event_xscale.c
+++ b/arch/arm/kernel/perf_event_xscale.c
@@ -59,7 +59,7 @@
 	[PERF_COUNT_HW_STALLED_CYCLES_BACKEND]	= HW_OP_UNSUPPORTED,
 };
 
-static const unsigned xscale_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
+static unsigned xscale_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]
 					   [PERF_COUNT_HW_CACHE_OP_MAX]
 					   [PERF_COUNT_HW_CACHE_RESULT_MAX] = {
 	[C(L1D)] = {
@@ -810,6 +810,8 @@
 	.id		= ARM_PERF_PMU_ID_XSCALE2,
 	.name		= "xscale2",
 	.handle_irq	= xscale2pmu_handle_irq,
+	.request_pmu_irq = armpmu_generic_request_irq,
+	.free_pmu_irq	= armpmu_generic_free_irq,
 	.enable		= xscale2pmu_enable_event,
 	.disable	= xscale2pmu_disable_event,
 	.read_counter	= xscale2pmu_read_counter,
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c
index 014f090..a9582f9 100644
--- a/arch/arm/kernel/process.c
+++ b/arch/arm/kernel/process.c
@@ -154,6 +154,9 @@
 	/* Push out any further dirty data, and ensure cache is empty */
 	flush_cache_all();
 
+	/* Push out the dirty data from external caches */
+	outer_disable();
+
 	/* Switch to the identity mapping. */
 	phys_reset = (phys_reset_t)(unsigned long)virt_to_phys(cpu_reset);
 	phys_reset((unsigned long)addr);
@@ -218,7 +221,8 @@
  * This is our default idle handler.
  */
 
-void (*arm_pm_idle)(void);
+extern void arch_idle(void);
+void (*arm_pm_idle)(void) = arch_idle;
 
 static void default_idle(void)
 {
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
index ebfac78..e30f1d8 100644
--- a/arch/arm/kernel/setup.c
+++ b/arch/arm/kernel/setup.c
@@ -103,6 +103,8 @@
 unsigned int elf_hwcap __read_mostly;
 EXPORT_SYMBOL(elf_hwcap);
 
+unsigned int boot_reason;
+EXPORT_SYMBOL(boot_reason);
 
 #ifdef MULTI_CPU
 struct processor processor __read_mostly;
@@ -959,6 +961,9 @@
 
 	parse_early_param();
 
+	if (mdesc->init_very_early)
+		mdesc->init_very_early();
+
 	sort(&meminfo.bank, meminfo.nr_banks, sizeof(meminfo.bank[0]), meminfo_cmp, NULL);
 	sanity_check_meminfo();
 	arm_memblock_init(&meminfo, mdesc);
@@ -1054,7 +1059,7 @@
 		   cpu_name, read_cpuid_id() & 15, elf_platform);
 
 #if defined(CONFIG_SMP)
-	for_each_online_cpu(i) {
+	for_each_present_cpu(i) {
 		/*
 		 * glibc reads /proc/cpuinfo to determine the number of
 		 * online processors, looking for lines beginning with
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
index f30d683..4738d71 100644
--- a/arch/arm/kernel/smp.c
+++ b/arch/arm/kernel/smp.c
@@ -51,6 +51,7 @@
 struct secondary_data secondary_data;
 
 enum ipi_msg_type {
+	IPI_CPU_START = 1,
 	IPI_TIMER = 2,
 	IPI_RESCHEDULE,
 	IPI_CALL_FUNC,
@@ -183,7 +184,7 @@
 		pr_err("CPU%u: cpu didn't die\n", cpu);
 		return;
 	}
-	printk(KERN_NOTICE "CPU%u: shutdown\n", cpu);
+	pr_debug("CPU%u: shutdown\n", cpu);
 
 	if (!platform_cpu_kill(cpu))
 		printk("CPU%u: unable to kill\n", cpu);
@@ -241,8 +242,6 @@
 	store_cpu_topology(cpuid);
 }
 
-static void percpu_timer_setup(void);
-
 /*
  * This is the secondary CPU boot entry.  We're using this CPUs
  * idle thread stack, but a set of temporary page tables.
@@ -263,7 +262,7 @@
 	enter_lazy_tlb(mm, current);
 	local_flush_tlb_all();
 
-	printk("CPU%u: Booted secondary processor\n", cpu);
+	pr_debug("CPU%u: Booted secondary processor\n", cpu);
 
 	cpu_init();
 	preempt_disable();
@@ -378,7 +377,8 @@
 }
 
 static const char *ipi_types[NR_IPI] = {
-#define S(x,s)	[x - IPI_TIMER] = s
+#define S(x,s)	[x - IPI_CPU_START] = s
+	S(IPI_CPU_START, "CPU start interrupts"),
 	S(IPI_TIMER, "Timer broadcast interrupts"),
 	S(IPI_RESCHEDULE, "Rescheduling interrupts"),
 	S(IPI_CALL_FUNC, "Function call interrupts"),
@@ -464,7 +464,7 @@
 }
 #endif
 
-static void __cpuinit percpu_timer_setup(void)
+void __cpuinit percpu_timer_setup(void)
 {
 	unsigned int cpu = smp_processor_id();
 	struct clock_event_device *evt = &per_cpu(percpu_clockevent, cpu);
@@ -581,10 +581,13 @@
 	unsigned int cpu = smp_processor_id();
 	struct pt_regs *old_regs = set_irq_regs(regs);
 
-	if (ipinr >= IPI_TIMER && ipinr < IPI_TIMER + NR_IPI)
-		__inc_irq_stat(cpu, ipi_irqs[ipinr - IPI_TIMER]);
+	if (ipinr >= IPI_CPU_START && ipinr < IPI_CPU_START + NR_IPI)
+		__inc_irq_stat(cpu, ipi_irqs[ipinr - IPI_CPU_START]);
 
 	switch (ipinr) {
+	case IPI_CPU_START:
+		/* Wake up from WFI/WFE using SGI */
+		break;
 	case IPI_TIMER:
 		irq_enter();
 		ipi_timer();
diff --git a/arch/arm/kernel/swp_emulate.c b/arch/arm/kernel/swp_emulate.c
index df74518..5d14485 100644
--- a/arch/arm/kernel/swp_emulate.c
+++ b/arch/arm/kernel/swp_emulate.c
@@ -174,6 +174,57 @@
 	return res;
 }
 
+static int check_condition(struct pt_regs *regs, unsigned int insn)
+{
+	unsigned int base_cond, neg, cond = 0;
+	unsigned int cpsr_z, cpsr_c, cpsr_n, cpsr_v;
+
+	cpsr_n = (regs->ARM_cpsr & PSR_N_BIT) ? 1 : 0;
+	cpsr_z = (regs->ARM_cpsr & PSR_Z_BIT) ? 1 : 0;
+	cpsr_c = (regs->ARM_cpsr & PSR_C_BIT) ? 1 : 0;
+	cpsr_v = (regs->ARM_cpsr & PSR_V_BIT) ? 1 : 0;
+
+	/* Upper 3 bits indicate condition, lower bit incicates negation */
+	base_cond = insn >> 29;
+	neg = insn & BIT(28) ? 1 : 0;
+
+	switch (base_cond) {
+	case 0x0:	/* equal */
+		cond = cpsr_z;
+		break;
+
+	case 0x1:	/* carry set */
+		cond = cpsr_c;
+		break;
+
+	case 0x2:	/* minus / negative */
+		cond = cpsr_n;
+		break;
+
+	case 0x3:	/* overflow */
+		cond = cpsr_v;
+		break;
+
+	case 0x4:	/* unsigned higher */
+		cond = (cpsr_c == 1) && (cpsr_z == 0);
+		break;
+
+	case 0x5:	/* signed greater / equal */
+		cond = (cpsr_n == cpsr_v);
+		break;
+
+	case 0x6:	/* signed greater */
+		cond = (cpsr_z == 0) && (cpsr_n == cpsr_v);
+		break;
+
+	case 0x7:	/* always */
+		cond = 1;
+		break;
+	};
+
+	return cond && !neg;
+}
+
 /*
  * swp_handler logs the id of calling process, dissects the instruction, sanity
  * checks the memory location, calls emulate_swpX for the actual operation and
@@ -207,6 +258,12 @@
 		previous_pid = current->pid;
 	}
 
+	/* Ignore the instruction if it fails its condition code check */
+	if (!check_condition(regs, instr)) {
+		regs->ARM_pc += 4;
+		return 0;
+	}
+
 	address = regs->uregs[EXTRACT_REG_NUM(instr, RN_OFFSET)];
 	data	= regs->uregs[EXTRACT_REG_NUM(instr, RT2_OFFSET)];
 	destreg = EXTRACT_REG_NUM(instr, RT_OFFSET);
diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S
index 43a31fb..89f7f23 100644
--- a/arch/arm/kernel/vmlinux.lds.S
+++ b/arch/arm/kernel/vmlinux.lds.S
@@ -8,7 +8,10 @@
 #include <asm/thread_info.h>
 #include <asm/memory.h>
 #include <asm/page.h>
-	
+#ifdef CONFIG_STRICT_MEMORY_RWX
+#include <asm/pgtable.h>
+#endif
+
 #define PROC_INFO							\
 	. = ALIGN(4);							\
 	VMLINUX_SYMBOL(__proc_info_begin) = .;				\
@@ -90,6 +93,7 @@
 		_text = .;
 		HEAD_TEXT
 	}
+
 	.text : {			/* Real text segment		*/
 		_stext = .;		/* Text and read-only data	*/
 			__exception_text_start = .;
@@ -111,6 +115,9 @@
 		*(.got)			/* Global offset table		*/
 			ARM_CPU_KEEP(PROC_INFO)
 	}
+#ifdef CONFIG_STRICT_MEMORY_RWX
+	. = ALIGN(1<<SECTION_SHIFT);
+#endif
 
 	RO_DATA(PAGE_SIZE)
 
@@ -134,7 +141,11 @@
 	_etext = .;			/* End of text and rodata section */
 
 #ifndef CONFIG_XIP_KERNEL
+#ifdef CONFIG_STRICT_MEMORY_RWX
+	. = ALIGN(1<<SECTION_SHIFT);
+#else
 	. = ALIGN(PAGE_SIZE);
+#endif
 	__init_begin = .;
 #endif
 
@@ -178,6 +189,10 @@
 		INIT_RAM_FS
 	}
 #ifndef CONFIG_XIP_KERNEL
+#ifdef CONFIG_STRICT_MEMORY_RWX
+	. = ALIGN(1<<SECTION_SHIFT);
+#endif
+	__init_data = .;
 	.exit.data : {
 		ARM_EXIT_KEEP(EXIT_DATA)
 	}
@@ -220,7 +235,7 @@
 		/*
 		 * The exception fixup table (might need resorting at runtime)
 		 */
-		. = ALIGN(4);
+		. = ALIGN(L1_CACHE_BYTES);
 		__start___ex_table = .;
 #ifdef CONFIG_MMU
 		*(__ex_table)