2011-08-30 20:41:05 -03:00
# include <linux/perf_event.h>
# include <linux/types.h>
# include "perf_event.h"
2010-02-26 12:05:05 +01:00
/*
* Not sure about some of these
*/
static const u64 p6_perfmon_event_map [ ] =
{
2012-10-19 17:31:54 -04:00
[ PERF_COUNT_HW_CPU_CYCLES ] = 0x0079 , /* CPU_CLK_UNHALTED */
[ PERF_COUNT_HW_INSTRUCTIONS ] = 0x00c0 , /* INST_RETIRED */
[ PERF_COUNT_HW_CACHE_REFERENCES ] = 0x0f2e , /* L2_RQSTS:M:E:S:I */
[ PERF_COUNT_HW_CACHE_MISSES ] = 0x012e , /* L2_RQSTS:I */
[ PERF_COUNT_HW_BRANCH_INSTRUCTIONS ] = 0x00c4 , /* BR_INST_RETIRED */
[ PERF_COUNT_HW_BRANCH_MISSES ] = 0x00c5 , /* BR_MISS_PRED_RETIRED */
[ PERF_COUNT_HW_BUS_CYCLES ] = 0x0062 , /* BUS_DRDY_CLOCKS */
[ PERF_COUNT_HW_STALLED_CYCLES_FRONTEND ] = 0x00a2 , /* RESOURCE_STALLS */
} ;
2013-03-11 09:56:05 +00:00
static const u64 __initconst p6_hw_cache_event_ids
2012-10-19 17:31:54 -04:00
[ PERF_COUNT_HW_CACHE_MAX ]
[ PERF_COUNT_HW_CACHE_OP_MAX ]
[ PERF_COUNT_HW_CACHE_RESULT_MAX ] =
{
[ C ( L1D ) ] = {
[ C ( OP_READ ) ] = {
[ C ( RESULT_ACCESS ) ] = 0x0043 , /* DATA_MEM_REFS */
[ C ( RESULT_MISS ) ] = 0x0045 , /* DCU_LINES_IN */
} ,
[ C ( OP_WRITE ) ] = {
[ C ( RESULT_ACCESS ) ] = 0 ,
[ C ( RESULT_MISS ) ] = 0x0f29 , /* L2_LD:M:E:S:I */
} ,
[ C ( OP_PREFETCH ) ] = {
[ C ( RESULT_ACCESS ) ] = 0 ,
[ C ( RESULT_MISS ) ] = 0 ,
} ,
} ,
[ C ( L1I ) ] = {
[ C ( OP_READ ) ] = {
[ C ( RESULT_ACCESS ) ] = 0x0080 , /* IFU_IFETCH */
[ C ( RESULT_MISS ) ] = 0x0f28 , /* L2_IFETCH:M:E:S:I */
} ,
[ C ( OP_WRITE ) ] = {
[ C ( RESULT_ACCESS ) ] = - 1 ,
[ C ( RESULT_MISS ) ] = - 1 ,
} ,
[ C ( OP_PREFETCH ) ] = {
[ C ( RESULT_ACCESS ) ] = 0 ,
[ C ( RESULT_MISS ) ] = 0 ,
} ,
} ,
[ C ( LL ) ] = {
[ C ( OP_READ ) ] = {
[ C ( RESULT_ACCESS ) ] = 0 ,
[ C ( RESULT_MISS ) ] = 0 ,
} ,
[ C ( OP_WRITE ) ] = {
[ C ( RESULT_ACCESS ) ] = 0 ,
[ C ( RESULT_MISS ) ] = 0x0025 , /* L2_M_LINES_INM */
} ,
[ C ( OP_PREFETCH ) ] = {
[ C ( RESULT_ACCESS ) ] = 0 ,
[ C ( RESULT_MISS ) ] = 0 ,
} ,
} ,
[ C ( DTLB ) ] = {
[ C ( OP_READ ) ] = {
[ C ( RESULT_ACCESS ) ] = 0x0043 , /* DATA_MEM_REFS */
[ C ( RESULT_MISS ) ] = 0 ,
} ,
[ C ( OP_WRITE ) ] = {
[ C ( RESULT_ACCESS ) ] = 0 ,
[ C ( RESULT_MISS ) ] = 0 ,
} ,
[ C ( OP_PREFETCH ) ] = {
[ C ( RESULT_ACCESS ) ] = 0 ,
[ C ( RESULT_MISS ) ] = 0 ,
} ,
} ,
[ C ( ITLB ) ] = {
[ C ( OP_READ ) ] = {
[ C ( RESULT_ACCESS ) ] = 0x0080 , /* IFU_IFETCH */
[ C ( RESULT_MISS ) ] = 0x0085 , /* ITLB_MISS */
} ,
[ C ( OP_WRITE ) ] = {
[ C ( RESULT_ACCESS ) ] = - 1 ,
[ C ( RESULT_MISS ) ] = - 1 ,
} ,
[ C ( OP_PREFETCH ) ] = {
[ C ( RESULT_ACCESS ) ] = - 1 ,
[ C ( RESULT_MISS ) ] = - 1 ,
} ,
} ,
[ C ( BPU ) ] = {
[ C ( OP_READ ) ] = {
[ C ( RESULT_ACCESS ) ] = 0x00c4 , /* BR_INST_RETIRED */
[ C ( RESULT_MISS ) ] = 0x00c5 , /* BR_MISS_PRED_RETIRED */
} ,
[ C ( OP_WRITE ) ] = {
[ C ( RESULT_ACCESS ) ] = - 1 ,
[ C ( RESULT_MISS ) ] = - 1 ,
} ,
[ C ( OP_PREFETCH ) ] = {
[ C ( RESULT_ACCESS ) ] = - 1 ,
[ C ( RESULT_MISS ) ] = - 1 ,
} ,
} ,
2010-02-26 12:05:05 +01:00
} ;
static u64 p6_pmu_event_map ( int hw_event )
{
return p6_perfmon_event_map [ hw_event ] ;
}
/*
* Event setting that is specified not to count anything .
* We use this to effectively disable a counter .
*
* L2_RQSTS with 0 MESI unit mask .
*/
# define P6_NOP_EVENT 0x0000002EULL
static struct event_constraint p6_event_constraints [ ] =
{
INTEL_EVENT_CONSTRAINT ( 0xc1 , 0x1 ) , /* FLOPS */
INTEL_EVENT_CONSTRAINT ( 0x10 , 0x1 ) , /* FP_COMP_OPS_EXE */
2012-10-19 17:30:01 -04:00
INTEL_EVENT_CONSTRAINT ( 0x11 , 0x2 ) , /* FP_ASSIST */
2010-02-26 12:05:05 +01:00
INTEL_EVENT_CONSTRAINT ( 0x12 , 0x2 ) , /* MUL */
INTEL_EVENT_CONSTRAINT ( 0x13 , 0x2 ) , /* DIV */
INTEL_EVENT_CONSTRAINT ( 0x14 , 0x1 ) , /* CYCLES_DIV_BUSY */
EVENT_CONSTRAINT_END
} ;
static void p6_pmu_disable_all ( void )
{
u64 val ;
/* p6 only has one enable register */
rdmsrl ( MSR_P6_EVNTSEL0 , val ) ;
2010-03-01 14:21:23 +01:00
val & = ~ ARCH_PERFMON_EVENTSEL_ENABLE ;
2010-02-26 12:05:05 +01:00
wrmsrl ( MSR_P6_EVNTSEL0 , val ) ;
}
2010-03-26 14:08:44 +01:00
static void p6_pmu_enable_all ( int added )
2010-02-26 12:05:05 +01:00
{
unsigned long val ;
/* p6 only has one enable register */
rdmsrl ( MSR_P6_EVNTSEL0 , val ) ;
2010-03-01 14:21:23 +01:00
val | = ARCH_PERFMON_EVENTSEL_ENABLE ;
2010-02-26 12:05:05 +01:00
wrmsrl ( MSR_P6_EVNTSEL0 , val ) ;
}
static inline void
2010-03-02 20:32:08 +01:00
p6_pmu_disable_event ( struct perf_event * event )
2010-02-26 12:05:05 +01:00
{
2010-03-02 20:32:08 +01:00
struct hw_perf_event * hwc = & event - > hw ;
2010-02-26 12:05:05 +01:00
u64 val = P6_NOP_EVENT ;
2012-06-07 13:32:04 -07:00
( void ) wrmsrl_safe ( hwc - > config_base , val ) ;
2010-02-26 12:05:05 +01:00
}
2010-03-02 20:32:08 +01:00
static void p6_pmu_enable_event ( struct perf_event * event )
2010-02-26 12:05:05 +01:00
{
2010-03-02 20:32:08 +01:00
struct hw_perf_event * hwc = & event - > hw ;
2010-02-26 12:05:05 +01:00
u64 val ;
val = hwc - > config ;
2012-10-19 17:33:38 -04:00
/*
* p6 only has a global event enable , set on PerfEvtSel0
* We " disable " events by programming P6_NOP_EVENT
* and we rely on p6_pmu_enable_all ( ) being called
* to actually enable the events .
*/
2010-02-26 12:05:05 +01:00
2012-06-07 13:32:04 -07:00
( void ) wrmsrl_safe ( hwc - > config_base , val ) ;
2010-02-26 12:05:05 +01:00
}
2012-03-15 20:09:14 +01:00
PMU_FORMAT_ATTR ( event , " config:0-7 " ) ;
PMU_FORMAT_ATTR ( umask , " config:8-15 " ) ;
PMU_FORMAT_ATTR ( edge , " config:18 " ) ;
PMU_FORMAT_ATTR ( pc , " config:19 " ) ;
PMU_FORMAT_ATTR ( inv , " config:23 " ) ;
PMU_FORMAT_ATTR ( cmask , " config:24-31 " ) ;
static struct attribute * intel_p6_formats_attr [ ] = {
& format_attr_event . attr ,
& format_attr_umask . attr ,
& format_attr_edge . attr ,
& format_attr_pc . attr ,
& format_attr_inv . attr ,
& format_attr_cmask . attr ,
NULL ,
} ;
2010-03-29 13:09:53 +02:00
static __initconst const struct x86_pmu p6_pmu = {
2010-02-26 12:05:05 +01:00
. name = " p6 " ,
. handle_irq = x86_pmu_handle_irq ,
. disable_all = p6_pmu_disable_all ,
. enable_all = p6_pmu_enable_all ,
. enable = p6_pmu_enable_event ,
. disable = p6_pmu_disable_event ,
2010-03-30 17:00:06 +02:00
. hw_config = x86_pmu_hw_config ,
2010-03-11 19:54:39 +03:00
. schedule_events = x86_schedule_events ,
2010-02-26 12:05:05 +01:00
. eventsel = MSR_P6_EVNTSEL0 ,
. perfctr = MSR_P6_PERFCTR0 ,
. event_map = p6_pmu_event_map ,
. max_events = ARRAY_SIZE ( p6_perfmon_event_map ) ,
. apic = 1 ,
. max_period = ( 1ULL < < 31 ) - 1 ,
. version = 0 ,
2010-03-29 18:36:50 +02:00
. num_counters = 2 ,
2010-02-26 12:05:05 +01:00
/*
* Events have 40 bits implemented . However they are designed such
* that bits [ 32 - 39 ] are sign extensions of bit 31. As such the
* effective width of a event for P6 - like PMU is 32 bits only .
*
* See IA - 32 Intel Architecture Software developer manual Vol 3 B
*/
2010-03-29 18:36:50 +02:00
. cntval_bits = 32 ,
. cntval_mask = ( 1ULL < < 32 ) - 1 ,
2010-02-26 12:05:05 +01:00
. get_event_constraints = x86_get_event_constraints ,
. event_constraints = p6_event_constraints ,
2012-03-15 20:09:14 +01:00
. format_attrs = intel_p6_formats_attr ,
2012-10-10 14:53:15 +02:00
. events_sysfs_show = intel_event_sysfs_show ,
2010-02-26 12:05:05 +01:00
} ;
2011-08-30 20:41:05 -03:00
__init int p6_pmu_init ( void )
2010-02-26 12:05:05 +01:00
{
switch ( boot_cpu_data . x86_model ) {
case 1 :
case 3 : /* Pentium Pro */
case 5 :
case 6 : /* Pentium II */
case 7 :
case 8 :
case 11 : /* Pentium III */
case 9 :
case 13 :
/* Pentium M */
break ;
default :
pr_cont ( " unsupported p6 CPU model %d " ,
boot_cpu_data . x86_model ) ;
return - ENODEV ;
}
x86_pmu = p6_pmu ;
2012-10-19 17:31:54 -04:00
memcpy ( hw_cache_event_ids , p6_hw_cache_event_ids ,
sizeof ( hw_cache_event_ids ) ) ;
2010-02-26 12:05:05 +01:00
return 0 ;
}