Initial Contribution

msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142

Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/arch/arm/perfmon/perf-smp.c b/arch/arm/perfmon/perf-smp.c
new file mode 100644
index 0000000..5417fc7
--- /dev/null
+++ b/arch/arm/perfmon/perf-smp.c
@@ -0,0 +1,751 @@
+/* Copyright (c) 2010, 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.
+ */
+
+/*
+perf-smp.c
+DESCRIPTION
+Manipulation, initialization of the ARMV7 Performance counter register.
+
+
+EXTERNALIZED FUNCTIONS
+
+INITIALIZATION AND SEQUENCING REQUIREMENTS
+*/
+
+/*
+INCLUDE FILES FOR MODULE
+*/
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/time.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include "l2_cp15_registers.h"
+
+/*
+DEFINITIONS AND DECLARATIONS FOR MODULE
+
+This section contains definitions for constants, macros, types, variables
+and other items needed by this module.
+*/
+
+/*
+  Constant / Define Declarations
+*/
+
+#define PM_NUM_COUNTERS 4
+#define L2_PM_ERR -1
+
+/*------------------------------------------------------------------------
+ * Global control bits
+------------------------------------------------------------------------*/
+#define PM_L2_GLOBAL_ENABLE     (1<<0)
+#define PM_L2_EVENT_RESET       (1<<1)
+#define PM_L2_CYCLE_RESET       (1<<2)
+#define PM_L2_CLKDIV            (1<<3)
+#define PM_L2_GLOBAL_TRACE      (1<<4)
+#define PM_L2_DISABLE_PROHIBIT  (1<<5)
+
+/*---------------------------------------------------------------------------
+ * Enable and clear bits for each event/trigger
+----------------------------------------------------------------------------*/
+#define PM_L2EV0_ENABLE        (1<<0)
+#define PM_L2EV1_ENABLE        (1<<1)
+#define PM_L2EV2_ENABLE        (1<<2)
+#define PM_L2EV3_ENABLE        (1<<3)
+#define PM_L2_COUNT_ENABLE      (1<<31)
+#define PM_L2_ALL_ENABLE        (0x8000000F)
+
+
+/*-----------------------------------------------------------------------------
+ * Overflow actions
+------------------------------------------------------------------------------*/
+#define PM_L2_OVERFLOW_NOACTION	(0)
+#define PM_L2_OVERFLOW_HALT	(1)
+#define PM_L2_OVERFLOW_STOP	(2)
+#define PM_L2_OVERFLOW_SKIP	(3)
+
+/*
+ * Shifts for each trigger type
+ */
+#define PM_STOP_SHIFT		24
+#define PM_RELOAD_SHIFT		22
+#define PM_RESUME_SHIFT		20
+#define PM_SUSPEND_SHIFT	18
+#define PM_START_SHIFT		16
+#define PM_STOPALL_SHIFT	15
+#define PM_STOPCOND_SHIFT	12
+#define PM_RELOADCOND_SHIFT	9
+#define PM_RESUMECOND_SHIFT	6
+#define PM_SUSPENDCOND_SHIFT	3
+#define PM_STARTCOND_SHIFT	0
+
+
+/*---------------------------------------------------------------------------
+External control register.  What todo when various events happen.
+Triggering events, etc.
+----------------------------------------------------------------------------*/
+#define PM_EXTTR0		0
+#define PM_EXTTR1		1
+#define PM_EXTTR2		2
+#define PM_EXTTR3		3
+
+#define PM_COND_NO_STOP		0
+#define PM_COND_STOP_CNTOVRFLW	1
+#define PM_COND_STOP_EXTERNAL	4
+#define PM_COND_STOP_TRACE	5
+#define PM_COND_STOP_EVOVRFLW	6
+#define PM_COND_STOP_EVTYPER	7
+
+/*--------------------------------------------------------------------------
+Protect against concurrent access.  There is an index register that is
+used to select the appropriate bank of registers.  If multiple processes
+are writting this at different times we could have a mess...
+---------------------------------------------------------------------------*/
+#define PM_LOCK()
+#define PM_UNLOCK()
+#define PRINT printk
+
+/*--------------------------------------------------------------------------
+The Event definitions
+--------------------------------------------------------------------------*/
+#define L2PM_EVT_PM0_EVT0	0x00
+#define L2PM_EVT_PM0_EVT1	0x01
+#define L2PM_EVT_PM0_EVT2	0x02
+#define L2PM_EVT_PM0_EVT3	0x03
+#define L2PM_EVT_PM1_EVT0	0x04
+#define L2PM_EVT_PM1_EVT1	0x05
+#define L2PM_EVT_PM1_EVT2	0x06
+#define L2PM_EVT_PM1_EVT3	0x07
+#define L2PM_EVT_PM2_EVT0	0x08
+#define L2PM_EVT_PM2_EVT1	0x09
+#define L2PM_EVT_PM2_EVT2	0x0a
+#define L2PM_EVT_PM2_EVT3	0x0b
+#define L2PM_EVT_PM3_EVT0	0x0c
+#define L2PM_EVT_PM3_EVT1	0x0d
+#define L2PM_EVT_PM3_EVT2	0x0e
+#define L2PM_EVT_PM3_EVT3	0x0f
+#define L2PM_EVT_PM4_EVT0	0x10
+#define L2PM_EVT_PM4_EVT1	0x11
+#define L2PM_EVT_PM4_EVT2	0x12
+#define L2PM_EVT_PM4_EVT3	0x13
+
+/*
+Type Declarations
+*/
+
+/*
+Local Object Definitions
+*/
+
+unsigned long l2_pm_cycle_overflow_count;
+unsigned long l2_pm_overflow_count[PM_NUM_COUNTERS];
+
+/*---------------------------------------------------------------------------
+Max number of events read from the config registers
+---------------------------------------------------------------------------*/
+static int pm_l2_max_events;
+
+static int irqid;
+
+/*
+Function Definitions
+*/
+
+/*
+FUNCTION  l2_pm_group_stop
+
+DESCRIPTION  Stop a group of the performance monitors.  Event monitor 0 is bit
+0, event monitor 1 bit 1, etc.  The cycle count can also be disable with
+bit 31.  Macros are provided for all of the indexes including an ALL.
+
+DEPENDENCIES
+
+RETURN VALUE
+None
+
+SIDE EFFECTS
+Stops the performance monitoring for the index passed.
+*/
+void pm_l2_group_stop(unsigned long mask)
+{
+	WCP15_L2PMCNTENCLR(mask);
+}
+
+/*
+FUNCTION  l2_pm_group_start
+
+DESCRIPTION  Start a group of the performance monitors.  Event monitor 0 is bit
+0, event monitor 1 bit 1, etc.  The cycle count can also be enabled with
+bit 31.  Macros are provided for all of the indexes including an ALL.
+
+DEPENDENCIES
+
+RETURN VALUE
+None
+
+SIDE EFFECTS
+Starts the performance monitoring for the index passed.
+*/
+void pm_l2_group_start(unsigned long mask)
+{
+	WCP15_L2PMCNTENSET(mask);
+}
+
+/*
+FUNCTION   l2_pm_get_overflow
+
+DESCRIPTION  Return the overflow condition for the index passed.
+
+DEPENDENCIES
+
+RETURN VALUE
+0 no overflow
+!0 (anything else) overflow;
+
+SIDE EFFECTS
+*/
+unsigned long l2_pm_get_overflow(int index)
+{
+  unsigned long overflow = 0;
+
+/*
+* Range check
+*/
+  if (index > pm_l2_max_events)
+	return L2_PM_ERR;
+  RCP15_L2PMOVSR(overflow);
+
+  return overflow & (1<<index);
+}
+
+/*
+FUNCTION  l2_pm_get_cycle_overflow
+
+DESCRIPTION
+Returns if the cycle counter has overflowed or not.
+
+DEPENDENCIES
+
+RETURN VALUE
+0 no overflow
+!0 (anything else) overflow;
+
+SIDE EFFECTS
+*/
+unsigned long l2_pm_get_cycle_overflow(void)
+{
+  unsigned long overflow = 0;
+
+  RCP15_L2PMOVSR(overflow);
+  return overflow & PM_L2_COUNT_ENABLE;
+}
+
+/*
+FUNCTION  l2_pm_reset_overflow
+
+DESCRIPTION Reset the cycle counter overflow bit.
+
+DEPENDENCIES
+
+RETURN VALUE
+None
+
+SIDE EFFECTS
+*/
+void l2_pm_reset_overflow(int index)
+{
+  WCP15_L2PMOVSR(1<<index);
+}
+
+/*
+FUNCTION  l2_pm_reset_cycle_overflow
+
+DESCRIPTION Reset the cycle counter overflow bit.
+
+DEPENDENCIES
+
+RETURN VALUE
+None
+
+SIDE EFFECTS
+*/
+void l2_pm_reset_cycle_overflow(void)
+{
+  WCP15_L2PMOVSR(PM_L2_COUNT_ENABLE);
+}
+
+/*
+FUNCTION l2_pm_get_cycle_count
+
+DESCRIPTION  return the count in the cycle count register.
+
+DEPENDENCIES
+
+RETURN VALUE
+The value in the cycle count register.
+
+SIDE EFFECTS
+*/
+unsigned long l2_pm_get_cycle_count(void)
+{
+  unsigned long cnt = 0;
+  RCP15_L2PMCCNTR(cnt);
+  return cnt;
+}
+
+/*
+FUNCTION  l2_pm_reset_cycle_count
+
+DESCRIPTION  reset the value in the cycle count register
+
+DEPENDENCIES
+
+RETURN VALUE
+NONE
+
+SIDE EFFECTS
+Resets the performance monitor cycle count register.
+Any interrupts period based on this overflow will be changed
+*/
+void l2_pm_reset_cycle_count(void)
+{
+  WCP15_L2PMCNTENCLR(PM_L2_COUNT_ENABLE);
+}
+
+/*
+FUNCTION  l2_pm_cycle_div_64
+
+DESCRIPTION  Set the cycle counter to count every 64th cycle instead of
+every cycle when the value passed is 1, otherwise counts every cycle.
+
+DEPENDENCIES
+
+RETURN VALUE
+none
+
+SIDE EFFECTS
+Changes the rate at which cycles are counted.  Anything that is reading
+the cycle count (pmGetCyucleCount) may get different results.
+*/
+void l2_pm_cycle_div_64(int enable)
+{
+	unsigned long enables = 0;
+
+	RCP15_L2PMCR(enables);
+	if (enable)
+		WCP15_L2PMCR(enables | PM_L2_CLKDIV);
+	else
+		WCP15_L2PMCR(enables & ~PM_L2_CLKDIV);
+}
+
+/*
+FUNCTION l2_pm_enable_cycle_counter
+
+DESCRIPTION  Enable the cycle counter.  Sets the bit in the enable register
+so the performance monitor counter starts up counting.
+
+DEPENDENCIES
+
+RETURN VALUE
+none
+
+SIDE EFFECTS
+*/
+void l2_pm_enable_cycle_counter(void)
+{
+/*
+*  Enable the counter.
+*/
+  WCP15_L2PMCNTENSET(PM_L2_COUNT_ENABLE);
+}
+
+/*
+FUNCTION l2_pm_disable_counter
+
+DESCRIPTION  Disable a single counter based on the index passed.
+
+DEPENDENCIES
+
+RETURN VALUE
+none
+
+SIDE EFFECTS
+Any triggers that are based on the stoped counter may not trigger...
+*/
+void l2_pm_disable_counter(int index)
+{
+  /*
+   * Range check
+   */
+  if (index > pm_l2_max_events)
+		return;
+  WCP15_L2PMCNTENCLR(1<<index);
+}
+
+/*
+FUNCTION l2_pm_enable_counter
+
+DESCRIPTION  Enable the counter with the index passed.
+
+DEPENDENCIES
+
+RETURN VALUE
+none.
+
+SIDE EFFECTS
+*/
+void l2_pm_enable_counter(int index)
+{
+  /*
+   * Range check
+   */
+  if (index > pm_l2_max_events)
+		return;
+  WCP15_L2PMCNTENSET(1<<index);
+}
+
+/*
+FUNCTION l2_pm_set_count
+
+DESCRIPTION  Set the number of events in a register, used for resets
+passed.
+
+DEPENDENCIES
+
+RETURN VALUE
+-1 if the index is out of range
+
+SIDE EFFECTS
+*/
+int l2_pm_set_count(int index, unsigned long new_value)
+{
+  unsigned long reg = 0;
+
+/*
+* Range check
+*/
+  if (index > pm_l2_max_events)
+		return L2_PM_ERR;
+
+/*
+* Lock, select the index and read the count...unlock
+*/
+  PM_LOCK();
+  WCP15_L2PMSELR(index);
+  WCP15_L2PMXEVCNTR(new_value);
+  PM_UNLOCK();
+  return reg;
+}
+
+int l2_pm_reset_count(int index)
+{
+  return l2_pm_set_count(index, 0);
+}
+
+/*
+FUNCTION l2_pm_get_count
+
+DESCRIPTION  Return the number of events that have happened for the index
+passed.
+
+DEPENDENCIES
+
+RETURN VALUE
+-1 if the index is out of range
+The number of events if inrange
+
+SIDE EFFECTS
+*/
+unsigned long l2_pm_get_count(int index)
+{
+  unsigned long reg = 0;
+
+/*
+* Range check
+*/
+  if (index > pm_l2_max_events)
+		return L2_PM_ERR;
+
+/*
+* Lock, select the index and read the count...unlock
+*/
+  PM_LOCK();
+  WCP15_L2PMSELR(index);
+  RCP15_L2PMXEVCNTR(reg);
+  PM_UNLOCK();
+  return reg;
+}
+
+unsigned long get_filter_code(unsigned long event)
+{
+	if (event == 0x0 || event == 0x4 || event == 0x08
+		|| event == 0x0c || event == 0x10)
+			return 0x0001003f;
+	else if (event == 0x1 || event == 0x5 || event == 0x09
+		|| event == 0x0d || event == 0x11)
+			return 0x0002003f;
+	else if (event == 0x2 || event == 0x6 || event == 0x0a
+		|| event == 0x0e || event == 0x12)
+			return 0x0004003f;
+	else if (event == 0x3 || event == 0x7 || event == 0x0b
+		|| event == 0x0f || event == 0x13)
+			return 0x0008003f;
+	else
+		return 0;
+}
+
+int l2_pm_set_event(int index, unsigned long event)
+{
+  unsigned long reg = 0;
+
+  /*
+   * Range check
+   */
+  if (index > pm_l2_max_events)
+		return L2_PM_ERR;
+
+  /*
+   * Lock, select the index and read the count...unlock
+   */
+  PM_LOCK();
+  WCP15_L2PMSELR(index);
+  WCP15_L2PMXEVTYPER(event);
+  /* WCP15_L2PMXEVFILTER(get_filter_code(event)); */
+  WCP15_L2PMXEVFILTER(0x000f003f);
+  PM_UNLOCK();
+  return reg;
+}
+
+/*
+FUNCTION  pm_set_local_bu
+
+DESCRIPTION  Set the local BU triggers.  Note that the MSB determines if
+  these are enabled or not.
+
+DEPENDENCIES
+
+RETURN VALUE
+  NONE
+
+SIDE EFFECTS
+*/
+void pm_set_local_bu(unsigned long value)
+{
+  WCP15_L2PMEVTYPER0(value);
+}
+
+/*
+FUNCTION  pm_set_local_cb
+
+DESCRIPTION  Set the local CB triggers.  Note that the MSB determines if
+  these are enabled or not.
+
+DEPENDENCIES
+
+RETURN VALUE
+  NONE
+
+SIDE EFFECTS
+*/
+void pm_set_local_cb(unsigned long value)
+{
+  WCP15_L2PMEVTYPER1(value);
+}
+
+/*
+FUNCTION  pm_set_local_mp
+
+DESCRIPTION  Set the local MP triggers.  Note that the MSB determines if
+  these are enabled or not.
+
+DEPENDENCIES
+
+RETURN VALUE
+  NONE
+
+SIDE EFFECTS
+*/
+void pm_set_local_mp(unsigned long value)
+{
+  WCP15_L2PMEVTYPER2(value);
+}
+
+/*
+FUNCTION  pm_set_local_sp
+
+DESCRIPTION  Set the local SP triggers.  Note that the MSB determines if
+  these are enabled or not.
+
+DEPENDENCIES
+
+RETURN VALUE
+  NONE
+
+SIDE EFFECTS
+*/
+void pm_set_local_sp(unsigned long value)
+{
+  WCP15_L2PMEVTYPER3(value);
+}
+
+/*
+FUNCTION  pm_set_local_scu
+
+DESCRIPTION  Set the local SCU triggers.  Note that the MSB determines if
+  these are enabled or not.
+
+DEPENDENCIES
+
+RETURN VALUE
+  NONE
+
+SIDE EFFECTS
+*/
+void pm_set_local_scu(unsigned long value)
+{
+  WCP15_L2PMEVTYPER4(value);
+}
+
+/*
+FUNCTION  l2_pm_isr
+
+DESCRIPTION:
+  Performance Monitor interrupt service routine to capture overflows
+
+DEPENDENCIES
+
+RETURN VALUE
+
+SIDE EFFECTS
+*/
+static irqreturn_t l2_pm_isr(int irq, void *d)
+{
+	int i;
+
+	for (i = 0; i < PM_NUM_COUNTERS; i++) {
+		if (l2_pm_get_overflow(i)) {
+			l2_pm_overflow_count[i]++;
+			l2_pm_reset_overflow(i);
+		}
+	}
+
+	if (l2_pm_get_cycle_overflow()) {
+		l2_pm_cycle_overflow_count++;
+		l2_pm_reset_cycle_overflow();
+	}
+
+	return IRQ_HANDLED;
+}
+
+
+void l2_pm_stop_all(void)
+{
+  WCP15_L2PMCNTENCLR(0xFFFFFFFF);
+}
+
+void l2_pm_reset_all(void)
+{
+  WCP15_L2PMCR(0xF);
+  WCP15_L2PMOVSR(PM_L2_ALL_ENABLE);  /* overflow clear */
+}
+
+void l2_pm_start_all(void)
+{
+  WCP15_L2PMCNTENSET(PM_L2_ALL_ENABLE);
+}
+
+/*
+FUNCTION l2_pm_initialize
+
+DESCRIPTION  Initialize the performanca monitoring for the v7 processor.
+  Ensures the cycle count is running and the event counters are enabled.
+
+DEPENDENCIES
+
+RETURN VALUE
+  NONE
+
+SIDE EFFECTS
+*/
+void l2_pm_initialize(void)
+{
+  unsigned long reg = 0;
+  unsigned char imp;
+  unsigned char id;
+  unsigned char num;
+  unsigned long enables = 0;
+  static int initialized;
+
+  if (initialized)
+		return;
+  initialized = 1;
+
+  irqid = SC_SICL2PERFMONIRPTREQ;
+  RCP15_L2PMCR(reg);
+  imp = (reg>>24) & 0xFF;
+  id  = (reg>>16) & 0xFF;
+  pm_l2_max_events = num = (reg>>11)  & 0xFF;
+  PRINT("V7 MP L2SCU Performance Monitor Capabilities\n");
+  PRINT("  Implementor %c(%d)\n", imp, imp);
+  PRINT("  Id %d %x\n", id, id);
+  PRINT("  Num Events %d %x\n", num, num);
+  PRINT("\nCycle counter enabled by default...\n");
+
+  /*
+   * Global enable, ensure the global enable is set so all
+   * subsequent actions take effect.  Also resets the counts
+   */
+  RCP15_L2PMCR(enables);
+  WCP15_L2PMCR(enables | PM_L2_GLOBAL_ENABLE | PM_L2_EVENT_RESET |
+      PM_L2_CYCLE_RESET | PM_L2_CLKDIV);
+
+  /*
+   * Enable access from user space
+   */
+
+  /*
+   * Install interrupt handler and the enable the interrupts
+   */
+  l2_pm_reset_cycle_overflow();
+  l2_pm_reset_overflow(0);
+  l2_pm_reset_overflow(1);
+  l2_pm_reset_overflow(2);
+  l2_pm_reset_overflow(3);
+  l2_pm_reset_overflow(4);
+
+	if (0 != request_irq(irqid, l2_pm_isr, 0, "l2perfmon", 0))
+		printk(KERN_ERR "%s:%d request_irq returned error\n",
+		__FILE__, __LINE__);
+  WCP15_L2PMINTENSET(PM_L2_ALL_ENABLE);
+  /*
+   * Enable the cycle counter.  Default, count 1:1 no divisor.
+   */
+  l2_pm_enable_cycle_counter();
+
+}
+
+void l2_pm_free_irq(void)
+{
+	free_irq(irqid, 0);
+}
+
+void l2_pm_deinitialize(void)
+{
+  unsigned long enables = 0;
+  RCP15_L2PMCR(enables);
+  WCP15_L2PMCR(enables & ~PM_L2_GLOBAL_ENABLE);
+}
+