blob: 614eedc92181a6f440419daf5146ea23a8f228b3 [file] [log] [blame]
/* 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-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);
}