2005-04-16 15:20:36 -07:00
/*
* Copyright ( C ) 2004 Anton Blanchard < anton @ au . ibm . com > , IBM
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*/
# include <linux/oprofile.h>
# include <linux/init.h>
# include <linux/smp.h>
# include <asm/ptrace.h>
# include <asm/processor.h>
# include <asm/cputable.h>
2005-09-06 14:55:35 +10:00
# include <asm/oprofile_impl.h>
2005-04-16 15:20:36 -07:00
# define dbg(args...)
static void ctrl_write ( unsigned int i , unsigned int val )
{
unsigned int tmp = 0 ;
unsigned long shift = 0 , mask = 0 ;
dbg ( " ctrl_write %d %x \n " , i , val ) ;
switch ( i ) {
case 0 :
tmp = mfspr ( SPRN_MMCR0 ) ;
shift = 6 ;
mask = 0x7F ;
break ;
case 1 :
tmp = mfspr ( SPRN_MMCR0 ) ;
shift = 0 ;
mask = 0x3F ;
break ;
case 2 :
tmp = mfspr ( SPRN_MMCR1 ) ;
shift = 31 - 4 ;
mask = 0x1F ;
break ;
case 3 :
tmp = mfspr ( SPRN_MMCR1 ) ;
shift = 31 - 9 ;
mask = 0x1F ;
break ;
case 4 :
tmp = mfspr ( SPRN_MMCR1 ) ;
shift = 31 - 14 ;
mask = 0x1F ;
break ;
case 5 :
tmp = mfspr ( SPRN_MMCR1 ) ;
shift = 31 - 19 ;
mask = 0x1F ;
break ;
case 6 :
tmp = mfspr ( SPRN_MMCR1 ) ;
shift = 31 - 24 ;
mask = 0x1F ;
break ;
case 7 :
tmp = mfspr ( SPRN_MMCR1 ) ;
shift = 31 - 28 ;
mask = 0xF ;
break ;
}
tmp = tmp & ~ ( mask < < shift ) ;
tmp | = val < < shift ;
switch ( i ) {
case 0 :
case 1 :
mtspr ( SPRN_MMCR0 , tmp ) ;
break ;
default :
mtspr ( SPRN_MMCR1 , tmp ) ;
}
dbg ( " ctrl_write mmcr0 %lx mmcr1 %lx \n " , mfspr ( SPRN_MMCR0 ) ,
mfspr ( SPRN_MMCR1 ) ) ;
}
static unsigned long reset_value [ OP_MAX_COUNTER ] ;
static int num_counters ;
2007-07-20 21:39:53 +02:00
static int rs64_reg_setup ( struct op_counter_config * ctr ,
2005-04-16 15:20:36 -07:00
struct op_system_config * sys ,
int num_ctrs )
{
int i ;
num_counters = num_ctrs ;
for ( i = 0 ; i < num_counters ; + + i )
reset_value [ i ] = 0x80000000UL - ctr [ i ] . count ;
/* XXX setup user and kernel profiling */
2007-07-20 21:39:53 +02:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2007-07-20 21:39:53 +02:00
static int rs64_cpu_setup ( struct op_counter_config * ctr )
2005-04-16 15:20:36 -07:00
{
unsigned int mmcr0 ;
/* reset MMCR0 and set the freeze bit */
mmcr0 = MMCR0_FC ;
mtspr ( SPRN_MMCR0 , mmcr0 ) ;
/* reset MMCR1, MMCRA */
mtspr ( SPRN_MMCR1 , 0 ) ;
if ( cpu_has_feature ( CPU_FTR_MMCRA ) )
mtspr ( SPRN_MMCRA , 0 ) ;
mmcr0 | = MMCR0_FCM1 | MMCR0_PMXE | MMCR0_FCECE ;
/* Only applies to POWER3, but should be safe on RS64 */
mmcr0 | = MMCR0_PMC1CE | MMCR0_PMCjCE ;
mtspr ( SPRN_MMCR0 , mmcr0 ) ;
dbg ( " setup on cpu %d, mmcr0 %lx \n " , smp_processor_id ( ) ,
mfspr ( SPRN_MMCR0 ) ) ;
dbg ( " setup on cpu %d, mmcr1 %lx \n " , smp_processor_id ( ) ,
mfspr ( SPRN_MMCR1 ) ) ;
2007-07-20 21:39:53 +02:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2007-07-20 21:39:53 +02:00
static int rs64_start ( struct op_counter_config * ctr )
2005-04-16 15:20:36 -07:00
{
int i ;
unsigned int mmcr0 ;
/* set the PMM bit (see comment below) */
mtmsrd ( mfmsr ( ) | MSR_PMM ) ;
for ( i = 0 ; i < num_counters ; + + i ) {
if ( ctr [ i ] . enabled ) {
2007-01-28 21:23:14 -06:00
classic_ctr_write ( i , reset_value [ i ] ) ;
2005-04-16 15:20:36 -07:00
ctrl_write ( i , ctr [ i ] . event ) ;
} else {
2007-01-28 21:23:14 -06:00
classic_ctr_write ( i , 0 ) ;
2005-04-16 15:20:36 -07:00
}
}
mmcr0 = mfspr ( SPRN_MMCR0 ) ;
/*
* now clear the freeze bit , counting will not start until we
* rfid from this excetion , because only at that point will
* the PMM bit be cleared
*/
mmcr0 & = ~ MMCR0_FC ;
mtspr ( SPRN_MMCR0 , mmcr0 ) ;
dbg ( " start on cpu %d, mmcr0 %x \n " , smp_processor_id ( ) , mmcr0 ) ;
2007-07-20 21:39:53 +02:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
static void rs64_stop ( void )
{
unsigned int mmcr0 ;
/* freeze counters */
mmcr0 = mfspr ( SPRN_MMCR0 ) ;
mmcr0 | = MMCR0_FC ;
mtspr ( SPRN_MMCR0 , mmcr0 ) ;
dbg ( " stop on cpu %d, mmcr0 %x \n " , smp_processor_id ( ) , mmcr0 ) ;
mb ( ) ;
}
static void rs64_handle_interrupt ( struct pt_regs * regs ,
struct op_counter_config * ctr )
{
unsigned int mmcr0 ;
2006-03-27 12:03:17 +11:00
int is_kernel ;
2005-04-16 15:20:36 -07:00
int val ;
int i ;
unsigned long pc = mfspr ( SPRN_SIAR ) ;
2006-03-27 12:03:17 +11:00
is_kernel = is_kernel_addr ( pc ) ;
2005-04-16 15:20:36 -07:00
/* set the PMM bit (see comment below) */
mtmsrd ( mfmsr ( ) | MSR_PMM ) ;
for ( i = 0 ; i < num_counters ; + + i ) {
2007-01-28 21:23:14 -06:00
val = classic_ctr_read ( i ) ;
2005-04-16 15:20:36 -07:00
if ( val < 0 ) {
if ( ctr [ i ] . enabled ) {
2006-03-27 12:03:17 +11:00
oprofile_add_ext_sample ( pc , regs , i , is_kernel ) ;
2007-01-28 21:23:14 -06:00
classic_ctr_write ( i , reset_value [ i ] ) ;
2005-04-16 15:20:36 -07:00
} else {
2007-01-28 21:23:14 -06:00
classic_ctr_write ( i , 0 ) ;
2005-04-16 15:20:36 -07:00
}
}
}
mmcr0 = mfspr ( SPRN_MMCR0 ) ;
/* reset the perfmon trigger */
mmcr0 | = MMCR0_PMXE ;
/*
* now clear the freeze bit , counting will not start until we
* rfid from this exception , because only at that point will
* the PMM bit be cleared
*/
mmcr0 & = ~ MMCR0_FC ;
mtspr ( SPRN_MMCR0 , mmcr0 ) ;
}
2005-09-19 23:18:31 +10:00
struct op_powerpc_model op_model_rs64 = {
2005-04-16 15:20:36 -07:00
. reg_setup = rs64_reg_setup ,
. cpu_setup = rs64_cpu_setup ,
. start = rs64_start ,
. stop = rs64_stop ,
. handle_interrupt = rs64_handle_interrupt ,
} ;