2005-04-17 02:20:36 +04:00
/*
2005-09-19 17:24:08 +04:00
* PPC 64 oprofile support :
2005-04-17 02:20:36 +04:00
* Copyright ( C ) 2004 Anton Blanchard < anton @ au . ibm . com > , IBM
2005-09-19 17:24:08 +04:00
* PPC 32 oprofile support : ( based on PPC 64 support )
* Copyright ( C ) Freescale Semiconductor , Inc 2004
* Author : Andy Fleming
2005-04-17 02:20:36 +04:00
*
* Based on alpha version .
*
* 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 <linux/errno.h>
# include <asm/ptrace.h>
# include <asm/system.h>
# include <asm/pmc.h>
2005-09-06 08:52:12 +04:00
# include <asm/cputable.h>
2005-09-06 08:55:35 +04:00
# include <asm/oprofile_impl.h>
2006-06-21 07:52:55 +04:00
# include <asm/firmware.h>
2005-04-17 02:20:36 +04:00
2005-09-19 17:18:31 +04:00
static struct op_powerpc_model * model ;
2005-04-17 02:20:36 +04:00
static struct op_counter_config ctr [ OP_MAX_COUNTER ] ;
static struct op_system_config sys ;
static void op_handle_interrupt ( struct pt_regs * regs )
{
model - > handle_interrupt ( regs , ctr ) ;
}
2005-09-19 17:24:08 +04:00
static int op_powerpc_setup ( void )
2005-04-17 02:20:36 +04:00
{
int err ;
/* Grab the hardware */
err = reserve_pmc_hardware ( op_handle_interrupt ) ;
if ( err )
return err ;
/* Pre-compute the values to stuff in the hardware registers. */
model - > reg_setup ( ctr , & sys , model - > num_counters ) ;
/* Configure the registers on all cpus. */
on_each_cpu ( model - > cpu_setup , NULL , 0 , 1 ) ;
return 0 ;
}
2005-09-19 17:24:08 +04:00
static void op_powerpc_shutdown ( void )
2005-04-17 02:20:36 +04:00
{
release_pmc_hardware ( ) ;
}
2005-09-19 17:24:08 +04:00
static void op_powerpc_cpu_start ( void * dummy )
2005-04-17 02:20:36 +04:00
{
model - > start ( ctr ) ;
}
2005-09-19 17:24:08 +04:00
static int op_powerpc_start ( void )
2005-04-17 02:20:36 +04:00
{
2005-09-19 17:24:08 +04:00
on_each_cpu ( op_powerpc_cpu_start , NULL , 0 , 1 ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-09-19 17:24:08 +04:00
static inline void op_powerpc_cpu_stop ( void * dummy )
2005-04-17 02:20:36 +04:00
{
model - > stop ( ) ;
}
2005-09-19 17:24:08 +04:00
static void op_powerpc_stop ( void )
2005-04-17 02:20:36 +04:00
{
2005-09-19 17:24:08 +04:00
on_each_cpu ( op_powerpc_cpu_stop , NULL , 0 , 1 ) ;
2005-04-17 02:20:36 +04:00
}
2005-09-19 17:24:08 +04:00
static int op_powerpc_create_files ( struct super_block * sb , struct dentry * root )
2005-04-17 02:20:36 +04:00
{
int i ;
2005-12-16 05:02:04 +03:00
# ifdef CONFIG_PPC64
2005-04-17 02:20:36 +04:00
/*
* There is one mmcr0 , mmcr1 and mmcra for setting the events for
* all of the counters .
*/
oprofilefs_create_ulong ( sb , root , " mmcr0 " , & sys . mmcr0 ) ;
oprofilefs_create_ulong ( sb , root , " mmcr1 " , & sys . mmcr1 ) ;
oprofilefs_create_ulong ( sb , root , " mmcra " , & sys . mmcra ) ;
2005-12-16 05:02:04 +03:00
# endif
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < model - > num_counters ; + + i ) {
struct dentry * dir ;
2006-06-26 11:24:34 +04:00
char buf [ 4 ] ;
2005-04-17 02:20:36 +04:00
snprintf ( buf , sizeof buf , " %d " , i ) ;
dir = oprofilefs_mkdir ( sb , root , buf ) ;
oprofilefs_create_ulong ( sb , dir , " enabled " , & ctr [ i ] . enabled ) ;
oprofilefs_create_ulong ( sb , dir , " event " , & ctr [ i ] . event ) ;
oprofilefs_create_ulong ( sb , dir , " count " , & ctr [ i ] . count ) ;
2005-12-16 05:02:04 +03:00
2005-04-17 02:20:36 +04:00
/*
2005-12-16 05:02:04 +03:00
* Classic PowerPC doesn ' t support per - counter
* control like this , but the options are
* expected , so they remain . For Freescale
* Book - E style performance monitors , we do
* support them .
2005-04-17 02:20:36 +04:00
*/
oprofilefs_create_ulong ( sb , dir , " kernel " , & ctr [ i ] . kernel ) ;
oprofilefs_create_ulong ( sb , dir , " user " , & ctr [ i ] . user ) ;
2005-09-19 17:24:08 +04:00
2005-04-17 02:20:36 +04:00
oprofilefs_create_ulong ( sb , dir , " unit_mask " , & ctr [ i ] . unit_mask ) ;
}
oprofilefs_create_ulong ( sb , root , " enable_kernel " , & sys . enable_kernel ) ;
oprofilefs_create_ulong ( sb , root , " enable_user " , & sys . enable_user ) ;
/* Default to tracing both kernel and user */
sys . enable_kernel = 1 ;
sys . enable_user = 1 ;
return 0 ;
}
int __init oprofile_arch_init ( struct oprofile_operations * ops )
{
2006-01-09 07:41:31 +03:00
if ( ! cur_cpu_spec - > oprofile_cpu_type )
2005-09-06 08:57:52 +04:00
return - ENODEV ;
2006-01-09 07:41:31 +03:00
2006-06-21 07:52:55 +04:00
if ( firmware_has_feature ( FW_FEATURE_ISERIES ) )
return - ENODEV ;
2006-01-09 07:41:31 +03:00
switch ( cur_cpu_spec - > oprofile_type ) {
# ifdef CONFIG_PPC64
2006-01-13 15:35:49 +03:00
case PPC_OPROFILE_RS64 :
2006-01-09 07:41:31 +03:00
model = & op_model_rs64 ;
break ;
2006-01-13 15:35:49 +03:00
case PPC_OPROFILE_POWER4 :
2006-01-09 07:41:31 +03:00
model = & op_model_power4 ;
break ;
# else
2006-01-13 15:35:49 +03:00
case PPC_OPROFILE_G4 :
2006-01-09 07:41:31 +03:00
model = & op_model_7450 ;
break ;
# endif
# ifdef CONFIG_FSL_BOOKE
2006-01-13 15:35:49 +03:00
case PPC_OPROFILE_BOOKE :
2006-01-09 07:41:31 +03:00
model = & op_model_fsl_booke ;
break ;
# endif
default :
return - ENODEV ;
}
2005-09-06 08:52:12 +04:00
model - > num_counters = cur_cpu_spec - > num_pmcs ;
2005-09-06 08:57:52 +04:00
ops - > cpu_type = cur_cpu_spec - > oprofile_cpu_type ;
2005-09-19 17:24:08 +04:00
ops - > create_files = op_powerpc_create_files ;
ops - > setup = op_powerpc_setup ;
ops - > shutdown = op_powerpc_shutdown ;
ops - > start = op_powerpc_start ;
ops - > stop = op_powerpc_stop ;
2006-03-27 04:57:01 +04:00
ops - > backtrace = op_powerpc_backtrace ;
2005-04-17 02:20:36 +04:00
2006-04-13 00:29:00 +04:00
printk ( KERN_DEBUG " oprofile: using %s performance monitoring. \n " ,
2005-04-17 02:20:36 +04:00
ops - > cpu_type ) ;
return 0 ;
}
void oprofile_arch_exit ( void )
{
}