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 [ ] =
{
[ PERF_COUNT_HW_CPU_CYCLES ] = 0x0079 ,
[ PERF_COUNT_HW_INSTRUCTIONS ] = 0x00c0 ,
[ PERF_COUNT_HW_CACHE_REFERENCES ] = 0x0f2e ,
[ PERF_COUNT_HW_CACHE_MISSES ] = 0x012e ,
[ PERF_COUNT_HW_BRANCH_INSTRUCTIONS ] = 0x00c4 ,
[ PERF_COUNT_HW_BRANCH_MISSES ] = 0x00c5 ,
[ PERF_COUNT_HW_BUS_CYCLES ] = 0x0062 ,
} ;
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 */
INTEL_EVENT_CONSTRAINT ( 0x11 , 0x1 ) , /* FP_ASSIST */
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
{
struct cpu_hw_events * cpuc = & __get_cpu_var ( cpu_hw_events ) ;
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 ;
if ( cpuc - > enabled )
2010-03-01 14:21:23 +01:00
val | = ARCH_PERFMON_EVENTSEL_ENABLE ;
2010-02-26 12:05:05 +01:00
2011-02-02 17:40:59 +01:00
( void ) checking_wrmsrl ( 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
{
struct cpu_hw_events * cpuc = & __get_cpu_var ( cpu_hw_events ) ;
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 ;
if ( cpuc - > enabled )
2010-03-01 14:21:23 +01:00
val | = ARCH_PERFMON_EVENTSEL_ENABLE ;
2010-02-26 12:05:05 +01:00
2011-02-02 17:40:59 +01:00
( void ) checking_wrmsrl ( 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 ,
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 ;
return 0 ;
}