1010 lines
21 KiB
C
1010 lines
21 KiB
C
|
/* 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);
|
||
|
}
|