2008-02-19 23:51:27 +01:00
/*
2008-07-22 21:08:48 +02:00
* @ file op_model_athlon . c
2007-12-18 18:05:58 +01:00
* athlon / K7 / K8 / Family 10 h model - specific MSR operations
2005-04-16 15:20:36 -07:00
*
2008-07-22 21:08:48 +02:00
* @ remark Copyright 2002 - 2008 OProfile authors
2005-04-16 15:20:36 -07:00
* @ remark Read the file COPYING
*
* @ author John Levon
* @ author Philippe Elie
* @ author Graydon Hoare
2008-07-22 21:08:48 +02:00
* @ author Robert Richter < robert . richter @ amd . com >
*/
2005-04-16 15:20:36 -07:00
# include <linux/oprofile.h>
# include <asm/ptrace.h>
# include <asm/msr.h>
2006-06-26 13:57:01 +02:00
# include <asm/nmi.h>
2008-02-19 23:51:27 +01:00
2005-04-16 15:20:36 -07:00
# include "op_x86_model.h"
# include "op_counter.h"
# define NUM_COUNTERS 4
# define NUM_CONTROLS 4
2008-02-19 23:51:27 +01:00
# define CTR_IS_RESERVED(msrs, c) (msrs->counters[(c)].addr ? 1 : 0)
# define CTR_READ(l, h, msrs, c) do {rdmsr(msrs->counters[(c)].addr, (l), (h)); } while (0)
# define CTR_WRITE(l, msrs, c) do {wrmsr(msrs->counters[(c)].addr, -(unsigned int)(l), -1); } while (0)
2005-04-16 15:20:36 -07:00
# define CTR_OVERFLOWED(n) (!((n) & (1U<<31)))
2008-02-19 23:51:27 +01:00
# define CTRL_IS_RESERVED(msrs, c) (msrs->controls[(c)].addr ? 1 : 0)
# define CTRL_READ(l, h, msrs, c) do {rdmsr(msrs->controls[(c)].addr, (l), (h)); } while (0)
# define CTRL_WRITE(l, h, msrs, c) do {wrmsr(msrs->controls[(c)].addr, (l), (h)); } while (0)
2005-04-16 15:20:36 -07:00
# define CTRL_SET_ACTIVE(n) (n |= (1<<22))
# define CTRL_SET_INACTIVE(n) (n &= ~(1<<22))
2007-12-18 18:05:58 +01:00
# define CTRL_CLEAR_LO(x) (x &= (1<<21))
# define CTRL_CLEAR_HI(x) (x &= 0xfffffcf0)
2005-04-16 15:20:36 -07:00
# define CTRL_SET_ENABLE(val) (val |= 1<<20)
2008-02-19 23:51:27 +01:00
# define CTRL_SET_USR(val, u) (val |= ((u & 1) << 16))
# define CTRL_SET_KERN(val, k) (val |= ((k & 1) << 17))
2005-04-16 15:20:36 -07:00
# define CTRL_SET_UM(val, m) (val |= (m << 8))
2007-12-18 18:05:58 +01:00
# define CTRL_SET_EVENT_LOW(val, e) (val |= (e & 0xff))
# define CTRL_SET_EVENT_HIGH(val, e) (val |= ((e >> 8) & 0xf))
# define CTRL_SET_HOST_ONLY(val, h) (val |= ((h & 1) << 9))
# define CTRL_SET_GUEST_ONLY(val, h) (val |= ((h & 1) << 8))
2005-04-16 15:20:36 -07:00
static unsigned long reset_value [ NUM_COUNTERS ] ;
2008-02-19 23:51:27 +01:00
2005-04-16 15:20:36 -07:00
static void athlon_fill_in_addresses ( struct op_msrs * const msrs )
{
2006-09-26 10:52:26 +02:00
int i ;
2008-02-19 23:51:27 +01:00
for ( i = 0 ; i < NUM_COUNTERS ; i + + ) {
2006-09-26 10:52:26 +02:00
if ( reserve_perfctr_nmi ( MSR_K7_PERFCTR0 + i ) )
msrs - > counters [ i ] . addr = MSR_K7_PERFCTR0 + i ;
else
msrs - > counters [ i ] . addr = 0 ;
}
2008-02-19 23:51:27 +01:00
for ( i = 0 ; i < NUM_CONTROLS ; i + + ) {
2006-09-26 10:52:26 +02:00
if ( reserve_evntsel_nmi ( MSR_K7_EVNTSEL0 + i ) )
msrs - > controls [ i ] . addr = MSR_K7_EVNTSEL0 + i ;
else
msrs - > controls [ i ] . addr = 0 ;
}
2005-04-16 15:20:36 -07:00
}
2008-02-19 23:51:27 +01:00
2005-04-16 15:20:36 -07:00
static void athlon_setup_ctrs ( struct op_msrs const * const msrs )
{
unsigned int low , high ;
int i ;
2008-02-19 23:51:27 +01:00
2005-04-16 15:20:36 -07:00
/* clear all counters */
for ( i = 0 ; i < NUM_CONTROLS ; + + i ) {
2008-02-19 23:51:27 +01:00
if ( unlikely ( ! CTRL_IS_RESERVED ( msrs , i ) ) )
2006-09-26 10:52:26 +02:00
continue ;
2005-04-16 15:20:36 -07:00
CTRL_READ ( low , high , msrs , i ) ;
2007-12-18 18:05:58 +01:00
CTRL_CLEAR_LO ( low ) ;
CTRL_CLEAR_HI ( high ) ;
2005-04-16 15:20:36 -07:00
CTRL_WRITE ( low , high , msrs , i ) ;
}
2006-09-26 10:52:26 +02:00
2005-04-16 15:20:36 -07:00
/* avoid a false detection of ctr overflows in NMI handler */
for ( i = 0 ; i < NUM_COUNTERS ; + + i ) {
2008-02-19 23:51:27 +01:00
if ( unlikely ( ! CTR_IS_RESERVED ( msrs , i ) ) )
2006-09-26 10:52:26 +02:00
continue ;
2005-04-16 15:20:36 -07:00
CTR_WRITE ( 1 , msrs , i ) ;
}
/* enable active counters */
for ( i = 0 ; i < NUM_COUNTERS ; + + i ) {
2008-02-19 23:51:27 +01:00
if ( ( counter_config [ i ] . enabled ) & & ( CTR_IS_RESERVED ( msrs , i ) ) ) {
2005-04-16 15:20:36 -07:00
reset_value [ i ] = counter_config [ i ] . count ;
CTR_WRITE ( counter_config [ i ] . count , msrs , i ) ;
CTRL_READ ( low , high , msrs , i ) ;
2007-12-18 18:05:58 +01:00
CTRL_CLEAR_LO ( low ) ;
CTRL_CLEAR_HI ( high ) ;
2005-04-16 15:20:36 -07:00
CTRL_SET_ENABLE ( low ) ;
CTRL_SET_USR ( low , counter_config [ i ] . user ) ;
CTRL_SET_KERN ( low , counter_config [ i ] . kernel ) ;
CTRL_SET_UM ( low , counter_config [ i ] . unit_mask ) ;
2007-12-18 18:05:58 +01:00
CTRL_SET_EVENT_LOW ( low , counter_config [ i ] . event ) ;
CTRL_SET_EVENT_HIGH ( high , counter_config [ i ] . event ) ;
CTRL_SET_HOST_ONLY ( high , 0 ) ;
CTRL_SET_GUEST_ONLY ( high , 0 ) ;
2005-04-16 15:20:36 -07:00
CTRL_WRITE ( low , high , msrs , i ) ;
} else {
reset_value [ i ] = 0 ;
}
}
}
2008-02-19 23:51:27 +01:00
2005-04-16 15:20:36 -07:00
static int athlon_check_ctrs ( struct pt_regs * const regs ,
struct op_msrs const * const msrs )
{
unsigned int low , high ;
int i ;
for ( i = 0 ; i < NUM_COUNTERS ; + + i ) {
2006-09-26 10:52:26 +02:00
if ( ! reset_value [ i ] )
continue ;
2005-04-16 15:20:36 -07:00
CTR_READ ( low , high , msrs , i ) ;
if ( CTR_OVERFLOWED ( low ) ) {
oprofile_add_sample ( regs , i ) ;
CTR_WRITE ( reset_value [ i ] , msrs , i ) ;
}
}
/* See op_model_ppro.c */
return 1 ;
}
2008-02-19 23:51:27 +01:00
2005-04-16 15:20:36 -07:00
static void athlon_start ( struct op_msrs const * const msrs )
{
unsigned int low , high ;
int i ;
for ( i = 0 ; i < NUM_COUNTERS ; + + i ) {
if ( reset_value [ i ] ) {
CTRL_READ ( low , high , msrs , i ) ;
CTRL_SET_ACTIVE ( low ) ;
CTRL_WRITE ( low , high , msrs , i ) ;
}
}
}
static void athlon_stop ( struct op_msrs const * const msrs )
{
2008-02-19 23:51:27 +01:00
unsigned int low , high ;
2005-04-16 15:20:36 -07:00
int i ;
/* Subtle: stop on all counters to avoid race with
* setting our pm callback */
for ( i = 0 ; i < NUM_COUNTERS ; + + i ) {
2006-09-26 10:52:26 +02:00
if ( ! reset_value [ i ] )
continue ;
2005-04-16 15:20:36 -07:00
CTRL_READ ( low , high , msrs , i ) ;
CTRL_SET_INACTIVE ( low ) ;
CTRL_WRITE ( low , high , msrs , i ) ;
}
}
2006-09-26 10:52:26 +02:00
static void athlon_shutdown ( struct op_msrs const * const msrs )
{
int i ;
for ( i = 0 ; i < NUM_COUNTERS ; + + i ) {
2008-02-19 23:51:27 +01:00
if ( CTR_IS_RESERVED ( msrs , i ) )
2006-09-26 10:52:26 +02:00
release_perfctr_nmi ( MSR_K7_PERFCTR0 + i ) ;
}
for ( i = 0 ; i < NUM_CONTROLS ; + + i ) {
2008-02-19 23:51:27 +01:00
if ( CTRL_IS_RESERVED ( msrs , i ) )
2006-09-26 10:52:26 +02:00
release_evntsel_nmi ( MSR_K7_EVNTSEL0 + i ) ;
}
}
2005-04-16 15:20:36 -07:00
2008-07-22 21:08:48 +02:00
static int op_amd_init ( struct oprofile_operations * ops )
{
return 0 ;
}
static void op_amd_exit ( void )
{
}
2005-04-16 15:20:36 -07:00
struct op_x86_model_spec const op_athlon_spec = {
2008-07-22 21:08:48 +02:00
. init = op_amd_init ,
. exit = op_amd_exit ,
2005-04-16 15:20:36 -07:00
. num_counters = NUM_COUNTERS ,
. num_controls = NUM_CONTROLS ,
. fill_in_addresses = & athlon_fill_in_addresses ,
. setup_ctrs = & athlon_setup_ctrs ,
. check_ctrs = & athlon_check_ctrs ,
. start = & athlon_start ,
2006-09-26 10:52:26 +02:00
. stop = & athlon_stop ,
. shutdown = & athlon_shutdown
2005-04-16 15:20:36 -07:00
} ;