/* Copyright (c) 2010, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

/*
perf-v7.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 "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 PM_V7_ERR -1

/*------------------------------------------------------------------------
 * Global control bits
------------------------------------------------------------------------*/
#define PM_GLOBAL_ENABLE     (1<<0)
#define PM_EVENT_RESET       (1<<1)
#define PM_CYCLE_RESET       (1<<2)
#define PM_CLKDIV            (1<<3)
#define PM_GLOBAL_TRACE      (1<<4)
#define PM_DISABLE_PROHIBIT  (1<<5)

/*---------------------------------------------------------------------------
 * Enable and clear bits for each event/trigger
----------------------------------------------------------------------------*/
#define PM_EV0_ENABLE        (1<<0)
#define PM_EV1_ENABLE        (1<<1)
#define PM_EV2_ENABLE        (1<<2)
#define PM_EV3_ENABLE        (1<<3)
#define PM_COUNT_ENABLE      (1<<31)
#define PM_ALL_ENABLE        (0x8000000F)


/*-----------------------------------------------------------------------------
 * Overflow actions
------------------------------------------------------------------------------*/
#define PM_OVERFLOW_NOACTION	(0)
#define PM_OVERFLOW_HALT	(1)
#define PM_OVERFLOW_STOP	(2)
#define PM_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 PM_EVT_SW_INCREMENT	0
#define PM_EVT_L1_I_MISS	1
#define PM_EVT_ITLB_MISS	2
#define PM_EVT_L1_D_MISS	3
#define PM_EVT_L1_D_ACCESS	4
#define PM_EVT_DTLB_MISS	5
#define PM_EVT_DATA_READ	6
#define PM_EVT_DATA_WRITE	7
#define PM_EVT_INSTRUCTION	8
#define PM_EVT_EXCEPTIONS	9
#define PM_EVT_EXCEPTION_RET	10
#define PM_EVT_CTX_CHANGE	11
#define PM_EVT_PC_CHANGE	12
#define PM_EVT_BRANCH		13
#define PM_EVT_RETURN		14
#define PM_EVT_UNALIGNED	15
#define PM_EVT_BRANCH_MISS	16
#define PM_EVT_EXTERNAL0	0x40
#define PM_EVT_EXTERNAL1	0x41
#define PM_EVT_EXTERNAL2	0x42
#define PM_EVT_EXTERNAL3	0x43
#define PM_EVT_TRACE0		0x44
#define PM_EVT_TRACE1		0x45
#define PM_EVT_TRACE2		0x46
#define PM_EVT_TRACE3		0x47
#define PM_EVT_PM0		0x48
#define PM_EVT_PM1		0x49
#define PM_EVT_PM2		0x4a
#define PM_EVT_PM3		0x4b
#define PM_EVT_LPM0_EVT0	0x4c
#define PM_EVT_LPM0_EVT1	0x4d
#define PM_EVT_LPM0_EVT2	0x4e
#define PM_EVT_LPM0_EVT3	0x4f
#define PM_EVT_LPM1_EVT0	0x50
#define PM_EVT_LPM1_EVT1	0x51
#define PM_EVT_LPM1_EVT2	0x52
#define PM_EVT_LPM1_EVT3	0x53
#define PM_EVT_LPM2_EVT0	0x54
#define PM_EVT_LPM2_EVT1	0x55
#define PM_EVT_LPM2_EVT2	0x56
#define PM_EVT_LPM2_EVT3	0x57
#define PM_EVT_L2_EVT0		0x58
#define PM_EVT_L2_EVT1		0x59
#define PM_EVT_L2_EVT2		0x5a
#define PM_EVT_L2_EVT3		0x5b
#define PM_EVT_VLP_EVT0		0x5c
#define PM_EVT_VLP_EVT1		0x5d
#define PM_EVT_VLP_EVT2		0x5e
#define PM_EVT_VLP_EVT3		0x5f

/*
Type Declarations
*/

/*--------------------------------------------------------------------------
A performance monitor trigger setup/initialization structure.  Contains
all of the fields necessary to setup a complex trigger with the internal
performance monitor.
---------------------------------------------------------------------------*/
struct pm_trigger_s {
  int index;
  int event_type;
  bool interrupt;
  bool overflow_enable;
  bool event_export;
  unsigned char overflow_action;
  unsigned char stop_index;
  unsigned char reload_index;
  unsigned char resume_index;
  unsigned char suspend_index;
  unsigned char start_index;
  bool  overflow_stop;
  unsigned char stop_condition;
  unsigned char reload_condition;
  unsigned char resume_condition;
  unsigned char suspend_condition;
  unsigned char start_condition;
};

/*
* Name and index place holder so we can display the event
*/
struct pm_name_s {
  unsigned long index;
  char          *name;
};

/*
Local Object Definitions
*/

unsigned long pm_cycle_overflow_count;
unsigned long pm_overflow_count[PM_NUM_COUNTERS];

/*---------------------------------------------------------------------------
Max number of events read from the config registers
---------------------------------------------------------------------------*/
static int pm_max_events;

/*--------------------------------------------------------------------------
Storage area for each of the triggers
*---------------------------------------------------------------------------*/
static struct pm_trigger_s pm_triggers[4];

/*--------------------------------------------------------------------------
Names and indexes of the events
--------------------------------------------------------------------------*/
static struct pm_name_s pm_names[] = {
  { PM_EVT_SW_INCREMENT,    "SW Increment"},
  { PM_EVT_L1_I_MISS,       "L1 I MISS"},
  { PM_EVT_ITLB_MISS,       "L1 ITLB MISS"},
  { PM_EVT_L1_D_MISS,       "L1 D MISS"},
  { PM_EVT_L1_D_ACCESS,     "L1 D ACCESS"},
  { PM_EVT_DTLB_MISS,       "DTLB MISS"},
  { PM_EVT_DATA_READ,       "DATA READ"},
  { PM_EVT_DATA_WRITE,      "DATA WRITE"},
  { PM_EVT_INSTRUCTION,     "INSTRUCTIONS"},
  { PM_EVT_EXCEPTIONS,      "EXCEPTIONS"},
  { PM_EVT_EXCEPTION_RET,   "EXCEPTION RETURN"},
  { PM_EVT_CTX_CHANGE,      "CTX CHANGE"},
  { PM_EVT_PC_CHANGE,       "PC CHANGE"},
  { PM_EVT_BRANCH,          "BRANCH"},
  { PM_EVT_RETURN,          "RETURN"},
  { PM_EVT_UNALIGNED,       "UNALIGNED"},
  { PM_EVT_BRANCH_MISS,     "BRANCH MISS"},
  { PM_EVT_EXTERNAL0,       "EXTERNAL 0"},
  { PM_EVT_EXTERNAL1,       "EXTERNAL 1"},
  { PM_EVT_EXTERNAL2,       "EXTERNAL 2"},
  { PM_EVT_EXTERNAL3,       "EXTERNAL 3"},
  { PM_EVT_TRACE0,          "TRACE 0"},
  { PM_EVT_TRACE1,          "TRACE 1"},
  { PM_EVT_TRACE2,          "TRACE 2"},
  { PM_EVT_TRACE3,          "TRACE 3"},
  { PM_EVT_PM0,             "PM0"},
  { PM_EVT_PM1,             "PM1"},
  { PM_EVT_PM2,             "PM2"},
  { PM_EVT_PM3,             "PM3"},
  { PM_EVT_LPM0_EVT0,       "LPM0 E0"},
  { PM_EVT_LPM0_EVT1,       "LPM0 E1"},
  { PM_EVT_LPM0_EVT2 ,      "LPM0 E2"},
  { PM_EVT_LPM0_EVT3,       "LPM0 E3"},
  { PM_EVT_LPM1_EVT0,       "LPM1 E0"},
  { PM_EVT_LPM1_EVT1,       "LPM1 E1"},
  { PM_EVT_LPM1_EVT2,       "LPM1 E2"},
  { PM_EVT_LPM1_EVT3,       "LPM1 E3"},
  { PM_EVT_LPM2_EVT0,       "LPM2 E0"},
  { PM_EVT_LPM2_EVT1 ,      "LPM2 E1"},
  { PM_EVT_LPM2_EVT2,       "LPM2 E2"},
  { PM_EVT_LPM2_EVT3,       "LPM2 E3"},
  { PM_EVT_L2_EVT0 ,        "L2 E0"},
  { PM_EVT_L2_EVT1,         "L2 E1"},
  { PM_EVT_L2_EVT2,         "L2 E2"},
  { PM_EVT_L2_EVT3 ,        "L2 E3"},
  { PM_EVT_VLP_EVT0 ,       "VLP E0"},
  { PM_EVT_VLP_EVT1,        "VLP E1"},
  { PM_EVT_VLP_EVT2,        "VLP E2"},
  { PM_EVT_VLP_EVT3,        "VLP E3"},
};

static int irqid;

/*
Function Definitions
*/

/*
FUNCTION  pm_find_event_name

DESCRIPTION Find the name associated with the event index passed and return
the pointer.

DEPENDENCIES

RETURN VALUE
Pointer to text string containing the name of the event or pointer to
an error string.  Either way access to the returned string will not
cause an access error.

SIDE EFFECTS
*/
char *pm_find_event_name(unsigned long index)
{
	unsigned long i = 0;

	while (pm_names[i].index != -1) {
		if (pm_names[i].index == index)
			return pm_names[i].name;
		i++;
	}
	return "BAD INDEX";
}

/*
FUNCTION  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 disabled 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_group_stop(unsigned long mask)
{
	WCP15_PMCNTENCLR(mask);
}

/*
FUNCTION  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_group_start(unsigned long mask)
{
	WCP15_PMCNTENSET(mask);
}

/*
FUNCTION pm_cycle_overflow_action

DESCRIPTION  Action to take for an overflow of the cycle counter.

DEPENDENCIES

RETURN VALUE
None

SIDE EFFECTS
Modify the state actions for overflow
*/
void pm_cycle_overflow_action(int action)
{
	unsigned long reg = 0;

	if ((action > PM_OVERFLOW_SKIP) || (action < 0))
		return;

	RCP15_PMACTLR(reg);
	reg &= ~(1<<30);   /*clear it*/
	WCP15_PMACTLR(reg | (action<<30));
}

/*
FUNCTION   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 pm_get_overflow(int index)
{
  unsigned long overflow = 0;

/*
* Range check
*/
  if (index > pm_max_events)
	return PM_V7_ERR;
  RCP15_PMOVSR(overflow);

  return overflow & (1<<index);
}

/*
FUNCTION  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 pm_get_cycle_overflow(void)
{
  unsigned long overflow = 0;

  RCP15_PMOVSR(overflow);
  return overflow & PM_COUNT_ENABLE;
}

/*
FUNCTION  pm_reset_overflow

DESCRIPTION Reset the cycle counter overflow bit.

DEPENDENCIES

RETURN VALUE
None

SIDE EFFECTS
*/
void pm_reset_overflow(int index)
{
  WCP15_PMOVSR(1<<index);
}

/*
FUNCTION  pm_reset_cycle_overflow

DESCRIPTION Reset the cycle counter overflow bit.

DEPENDENCIES

RETURN VALUE
None

SIDE EFFECTS
*/
void pm_reset_cycle_overflow(void)
{
  WCP15_PMOVSR(PM_COUNT_ENABLE);
}

/*
FUNCTION 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 pm_get_cycle_count(void)
{
  unsigned long cnt = 0;
  RCP15_PMCCNTR(cnt);
  return cnt;
}

/*
FUNCTION  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 pm_reset_cycle_count(void)
{
  WCP15_PMCNTENCLR(PM_COUNT_ENABLE);
}

/*
FUNCTION  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 pm_cycle_div_64(int enable)
{
	unsigned long enables = 0;

	RCP15_PMCR(enables);
	if (enable)
		WCP15_PMCR(enables | PM_CLKDIV);
	else
		WCP15_PMCR(enables & ~PM_CLKDIV);
}

/*
FUNCTION 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 pm_enable_cycle_counter(void)
{
/*
*  Enable the counter.
*/
  WCP15_PMCNTENSET(PM_COUNT_ENABLE);
}

/*
FUNCTION 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 pm_disable_counter(int index)
{
  /*
   * Range check
   */
  if (index > pm_max_events)
		return;
  WCP15_PMCNTENCLR(1<<index);
}

/*
FUNCTION pm_enable_counter

DESCRIPTION  Enable the counter with the index passed.

DEPENDENCIES

RETURN VALUE
none.

SIDE EFFECTS
*/
void pm_enable_counter(int index)
{
  /*
   * Range check
   */
  if (index > pm_max_events)
		return;
  WCP15_PMCNTENSET(1<<index);
}

/*
FUNCTION 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 pm_set_count(int index, unsigned long new_value)
{
  unsigned long reg = 0;

/*
* Range check
*/
  if (index > pm_max_events)
		return PM_V7_ERR;

/*
* Lock, select the index and read the count...unlock
*/
  PM_LOCK();
  WCP15_PMSELR(index);
  WCP15_PMXEVCNTR(new_value);
  PM_UNLOCK();
  return reg;
}

int pm_reset_count(int index)
{
  return pm_set_count(index, 0);
}

/*
FUNCTION 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 pm_get_count(int index)
{
  unsigned long reg = 0;

/*
* Range check
*/
  if (index > pm_max_events)
		return PM_V7_ERR;

/*
* Lock, select the index and read the count...unlock
*/
  PM_LOCK();
  WCP15_PMSELR(index);
  RCP15_PMXEVCNTR(reg);
  PM_UNLOCK();
  return reg;
}

/*
FUNCTION pm_show_event_info

DESCRIPTION Display (print) the information about the event at the index
passed.  Shows the index, name and count if a valid index is passed.  If
the index is not valid, then nothing is displayed.

DEPENDENCIES

RETURN VALUE
None

SIDE EFFECTS
*/
void pm_show_event_info(unsigned long index)
{
  unsigned long count;
  unsigned long event_type;

  if (index > pm_max_events)
		return;
  if (pm_triggers[index].index > pm_max_events)
		return;

  count = pm_get_count(index);
  event_type = pm_triggers[index].event_type;

  PRINT("Event %ld Trigger %s(%ld) count:%ld\n", index,
     pm_find_event_name(event_type), event_type, count);
}

/*
FUNCTION  pm_event_init

DESCRIPTION  Given the struct pm_trigger_s info passed, configure the event.
This can be a complex trigger or a simple trigger.  Any old values in the
event are lost.

DEPENDENCIES

RETURN VALUE
status

SIDE EFFECTS
stops and clears the event at the index passed.
*/
int pm_event_init(struct pm_trigger_s *data)
{
  unsigned long trigger;
  unsigned long actlr = 0;

  if (0 == data)
		return PM_V7_ERR;
  if (data->index > pm_max_events)
		return PM_V7_ERR;

  /*
   * Setup the trigger based ont he passed values
   */
  trigger = ((data->overflow_enable&1)<<31) |
	((data->event_export&1)<<30) |
	((data->stop_index&3)<<PM_STOP_SHIFT) |
	((data->reload_index&3)<<PM_RELOAD_SHIFT) |
	((data->resume_index&3)<<PM_RESUME_SHIFT) |
	((data->suspend_index&3)<<PM_SUSPEND_SHIFT) |
	((data->start_index&3)<<PM_START_SHIFT) |
	((data->overflow_stop&1)<<PM_STOPALL_SHIFT) |
	((data->stop_condition&7)<<PM_STOPCOND_SHIFT) |
	((data->reload_condition&7)<<PM_RELOADCOND_SHIFT) |
	((data->resume_condition&7)<<PM_RESUMECOND_SHIFT) |
	((data->suspend_condition&7)<<PM_SUSPENDCOND_SHIFT) |
	((data->start_condition&7)<<PM_STARTCOND_SHIFT);

  /*
   * Disable this counter while we are updating.
   */
  pm_disable_counter(data->index);

  /*
   * Lock, select the bank, set the trigger event and the event type
   * then unlock.
   */
  PM_LOCK();
  RCP15_PMACTLR(actlr);
  actlr &= ~(3<<(data->index<<1));
  WCP15_PMACTLR(actlr | ((data->overflow_action&3) << (data->index<<1)));
  WCP15_PMSELR(data->index);
  WCP15_PMXEVTYPER(data->event_type);
  WCP15_PMXEVCNTCR(trigger);
  PM_UNLOCK();

  /*
   * Make a copy of the trigger so we know what it is when/if it triggers.
   */
  memcpy(&pm_triggers[data->index], data, sizeof(*data));

  /*
   * We do not re-enable this here so events can be started together with
   * pm_group_start() that way an accurate measure can be taken...
   */

  return 0;
}

int pm_set_event(int index, unsigned long event)
{
  unsigned long reg = 0;

  /*
   * Range check
   */
  if (index > pm_max_events)
		return PM_V7_ERR;

  /*
   * Lock, select the index and read the count...unlock
   */
  PM_LOCK();
  WCP15_PMSELR(index);
  WCP15_PMXEVTYPER(event);
  PM_UNLOCK();
  return reg;
}

/*
FUNCTION  pm_set_local_iu

DESCRIPTION  Set the local IU triggers.  Note that the MSB determines if
  these are enabled or not.

DEPENDENCIES

RETURN VALUE
  NONE

SIDE EFFECTS
*/
void pm_set_local_iu(unsigned long value)
{
  WCP15_LPM0EVTYPER(value);
}

/*
FUNCTION  pm_set_local_iu

DESCRIPTION  Set the local IU triggers.  Note that the MSB determines if
  these are enabled or not.

DEPENDENCIES

RETURN VALUE
  NONE

SIDE EFFECTS
*/
void pm_set_local_xu(unsigned long value)
{
  WCP15_LPM1EVTYPER(value);
}

/*
FUNCTION  pm_set_local_su

DESCRIPTION  Set the local SU triggers.  Note that the MSB determines if
  these are enabled or not.

DEPENDENCIES

RETURN VALUE
  NONE

SIDE EFFECTS
*/
void pm_set_local_su(unsigned long value)
{
  WCP15_LPM2EVTYPER(value);
}

/*
FUNCTION  pm_set_local_l2

DESCRIPTION  Set the local L2 triggers.  Note that the MSB determines if
  these are enabled or not.

DEPENDENCIES

RETURN VALUE
  NONE

SIDE EFFECTS
*/
void pm_set_local_l2(unsigned long value)
{
  WCP15_L2LPMEVTYPER(value);
}

/*
FUNCTION  pm_set_local_vu

DESCRIPTION  Set the local VU triggers.  Note that the MSB determines if
  these are enabled or not.

DEPENDENCIES

RETURN VALUE
  NONE

SIDE EFFECTS
*/
void pm_set_local_vu(unsigned long value)
{
  WCP15_VLPMEVTYPER(value);
}

/*
FUNCTION  pm_isr

DESCRIPTION:
  Performance Monitor interrupt service routine to capture overflows

DEPENDENCIES

RETURN VALUE

SIDE EFFECTS
*/
static irqreturn_t pm_isr(int irq, void *d)
{
	int i;

	for (i = 0; i < PM_NUM_COUNTERS; i++) {
		if (pm_get_overflow(i)) {
			pm_overflow_count[i]++;
			pm_reset_overflow(i);
		}
	}

	if (pm_get_cycle_overflow()) {
		pm_cycle_overflow_count++;
		pm_reset_cycle_overflow();
	}

	return IRQ_HANDLED;
}


void pm_stop_all(void)
{
  WCP15_PMCNTENCLR(0xFFFFFFFF);
}

void pm_reset_all(void)
{
  WCP15_PMCR(0xF);
  WCP15_PMOVSR(PM_ALL_ENABLE);  /* overflow clear */
}

void pm_start_all(void)
{
  WCP15_PMCNTENSET(PM_ALL_ENABLE);
}

/*
FUNCTION 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 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 = INT_ARMQC_PERFMON;
  RCP15_PMCR(reg);
  imp = (reg>>24) & 0xFF;
  id  = (reg>>16) & 0xFF;
  pm_max_events = num = (reg>>11)  & 0xFF;
  PRINT("V7Performance 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_PMCR(enables);
  WCP15_PMCR(enables | PM_GLOBAL_ENABLE | PM_EVENT_RESET |
      PM_CYCLE_RESET | PM_CLKDIV);

  /*
   * Enable access from user space
   */
  WCP15_PMUSERENR(1);
  WCP15_PMACTLR(1);

  /*
   * Install interrupt handler and the enable the interrupts
   */
  pm_reset_cycle_overflow();
  pm_reset_overflow(0);
  pm_reset_overflow(1);
  pm_reset_overflow(2);
  pm_reset_overflow(3);

	if (0 != request_irq(irqid, pm_isr, 0, "perfmon", 0))
		printk(KERN_ERR "%s:%d request_irq returned error\n",
		__FILE__, __LINE__);
  WCP15_PMINTENSET(PM_ALL_ENABLE);
  /*
   * Enable the cycle counter.  Default, count 1:1 no divisor.
   */
  pm_enable_cycle_counter();

}

void pm_free_irq(void)
{
	free_irq(irqid, 0);
}

void pm_deinitialize(void)
{
  unsigned long enables = 0;
  RCP15_PMCR(enables);
  WCP15_PMCR(enables & ~PM_GLOBAL_ENABLE);
}
