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);
+}
+