2005-04-16 15:20:36 -07:00
/*
* arch / sh / oprofile / op_model_sh7750 . c
*
* OProfile support for SH7750 / SH7750S Performance Counters
*
2008-12-16 09:36:25 +09:00
* Copyright ( C ) 2003 - 2008 Paul Mundt
2005-04-16 15:20:36 -07:00
*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*/
# include <linux/kernel.h>
# include <linux/oprofile.h>
# include <linux/profile.h>
# include <linux/init.h>
# include <linux/errno.h>
# include <linux/interrupt.h>
2008-12-16 09:36:25 +09:00
# include <linux/io.h>
2005-04-16 15:20:36 -07:00
# include <linux/fs.h>
2008-12-16 09:36:25 +09:00
# include "op_impl.h"
2005-04-16 15:20:36 -07:00
# define PM_CR_BASE 0xff000084 /* 16-bit */
# define PM_CTR_BASE 0xff100004 /* 32-bit */
2008-12-16 09:36:25 +09:00
# define PMCR(n) (PM_CR_BASE + ((n) * 0x04))
# define PMCTRH(n) (PM_CTR_BASE + 0x00 + ((n) * 0x08))
# define PMCTRL(n) (PM_CTR_BASE + 0x04 + ((n) * 0x08))
2005-04-16 15:20:36 -07:00
# define PMCR_PMM_MASK 0x0000003f
# define PMCR_CLKF 0x00000100
# define PMCR_PMCLR 0x00002000
# define PMCR_PMST 0x00004000
# define PMCR_PMEN 0x00008000
2008-12-16 09:36:25 +09:00
struct op_sh_model op_model_sh7750_ops ;
2005-04-16 15:20:36 -07:00
# define NR_CNTRS 2
2008-12-16 09:36:25 +09:00
static struct sh7750_ppc_register_config {
unsigned int ctrl ;
unsigned long cnt_hi ;
unsigned long cnt_lo ;
} regcache [ NR_CNTRS ] ;
2005-04-16 15:20:36 -07:00
/*
* There are a number of events supported by each counter ( 33 in total ) .
* Since we have 2 counters , each counter will take the event code as it
* corresponds to the PMCR PMM setting . Each counter can be configured
* independently .
*
* Event Code Description
* - - - - - - - - - - - - - - - - - - - - -
*
* 0x01 Operand read access
* 0x02 Operand write access
* 0x03 UTLB miss
* 0x04 Operand cache read miss
* 0x05 Operand cache write miss
* 0x06 Instruction fetch ( w / cache )
* 0x07 Instruction TLB miss
* 0x08 Instruction cache miss
* 0x09 All operand accesses
* 0x0a All instruction accesses
* 0x0b OC RAM operand access
* 0x0d On - chip I / O space access
* 0x0e Operand access ( r / w )
* 0x0f Operand cache miss ( r / w )
* 0x10 Branch instruction
* 0x11 Branch taken
* 0x12 BSR / BSRF / JSR
* 0x13 Instruction execution
* 0x14 Instruction execution in parallel
* 0x15 FPU Instruction execution
* 0x16 Interrupt
* 0x17 NMI
* 0x18 trapa instruction execution
* 0x19 UBCA match
* 0x1a UBCB match
* 0x21 Instruction cache fill
* 0x22 Operand cache fill
* 0x23 Elapsed time
* 0x24 Pipeline freeze by I - cache miss
* 0x25 Pipeline freeze by D - cache miss
* 0x27 Pipeline freeze by branch instruction
* 0x28 Pipeline freeze by CPU register
* 0x29 Pipeline freeze by FPU
*
* Unfortunately we don ' t have a native exception or interrupt for counter
* overflow ( although since these counters can run for 16.3 days without
* overflowing , it ' s not really necessary ) .
*
* OProfile on the other hand likes to have samples taken periodically , so
* for now we just piggyback the timer interrupt to get the expected
* behavior .
*/
2007-10-30 18:24:17 +09:00
static int sh7750_timer_notify ( struct pt_regs * regs )
2005-04-16 15:20:36 -07:00
{
2007-10-30 18:24:17 +09:00
oprofile_add_sample ( regs , 0 ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
static u64 sh7750_read_counter ( int counter )
{
2008-12-16 09:36:25 +09:00
return ( u64 ) ( ( u64 ) ( __raw_readl ( PMCTRH ( counter ) ) & 0xffff ) < < 32 ) |
__raw_readl ( PMCTRL ( counter ) ) ;
2005-04-16 15:20:36 -07:00
}
/*
* Files will be in a path like :
*
* / < oprofilefs mount point > / < counter number > / < file >
*
* So when dealing with < file > , we look to the parent dentry for the counter
* number .
*/
static inline int to_counter ( struct file * file )
{
2006-12-08 02:37:38 -08:00
const unsigned char * name = file - > f_path . dentry - > d_parent - > d_name . name ;
2005-04-16 15:20:36 -07:00
return ( int ) simple_strtol ( name , NULL , 10 ) ;
}
/*
* XXX : We have 48 - bit counters , so we ' re probably going to want something
* more along the lines of oprofilefs_ullong_to_user ( ) . . Truncating to
* unsigned long works fine for now though , as long as we don ' t attempt to
* profile for too horribly long .
*/
static ssize_t sh7750_read_count ( struct file * file , char __user * buf ,
size_t count , loff_t * ppos )
{
int counter = to_counter ( file ) ;
u64 val = sh7750_read_counter ( counter ) ;
return oprofilefs_ulong_to_user ( ( unsigned long ) val , buf , count , ppos ) ;
}
static ssize_t sh7750_write_count ( struct file * file , const char __user * buf ,
size_t count , loff_t * ppos )
{
int counter = to_counter ( file ) ;
unsigned long val ;
if ( oprofilefs_ulong_from_user ( & val , buf , count ) )
return - EFAULT ;
/*
* Any write will clear the counter , although only 0 should be
* written for this purpose , as we do not support setting the
* counter to an arbitrary value .
*/
WARN_ON ( val ! = 0 ) ;
2008-12-16 09:36:25 +09:00
__raw_writew ( __raw_readw ( PMCR ( counter ) ) | PMCR_PMCLR , PMCR ( counter ) ) ;
2005-04-16 15:20:36 -07:00
return count ;
}
2007-02-12 00:55:31 -08:00
static const struct file_operations count_fops = {
2005-04-16 15:20:36 -07:00
. read = sh7750_read_count ,
. write = sh7750_write_count ,
} ;
2008-12-16 09:36:25 +09:00
static int sh7750_ppc_create_files ( struct super_block * sb , struct dentry * dir )
2005-04-16 15:20:36 -07:00
{
2008-12-16 09:36:25 +09:00
return oprofilefs_create_file ( sb , dir , " count " , & count_fops ) ;
}
2005-04-16 15:20:36 -07:00
2008-12-16 09:36:25 +09:00
static void sh7750_ppc_reg_setup ( struct op_counter_config * ctr )
{
unsigned int counters = op_model_sh7750_ops . num_counters ;
int i ;
2005-04-16 15:20:36 -07:00
2008-12-16 09:36:25 +09:00
for ( i = 0 ; i < counters ; i + + ) {
regcache [ i ] . ctrl = 0 ;
regcache [ i ] . cnt_hi = 0 ;
regcache [ i ] . cnt_lo = 0 ;
2005-04-16 15:20:36 -07:00
2008-12-16 09:36:25 +09:00
if ( ! ctr [ i ] . enabled )
continue ;
2005-04-16 15:20:36 -07:00
2008-12-16 09:36:25 +09:00
regcache [ i ] . ctrl | = ctr [ i ] . event | PMCR_PMEN | PMCR_PMST ;
regcache [ i ] . cnt_hi = ( unsigned long ) ( ( ctr - > count > > 32 ) & 0xffff ) ;
regcache [ i ] . cnt_lo = ( unsigned long ) ( ctr - > count & 0xffffffff ) ;
2005-04-16 15:20:36 -07:00
}
}
2008-12-16 09:36:25 +09:00
static void sh7750_ppc_cpu_setup ( void * args )
2005-04-16 15:20:36 -07:00
{
2008-12-16 09:36:25 +09:00
unsigned int counters = op_model_sh7750_ops . num_counters ;
int i ;
2005-04-16 15:20:36 -07:00
2008-12-16 09:36:25 +09:00
for ( i = 0 ; i < counters ; i + + ) {
__raw_writew ( 0 , PMCR ( i ) ) ;
__raw_writel ( regcache [ i ] . cnt_hi , PMCTRH ( i ) ) ;
__raw_writel ( regcache [ i ] . cnt_lo , PMCTRL ( i ) ) ;
2005-04-16 15:20:36 -07:00
}
}
2008-12-16 09:36:25 +09:00
static void sh7750_ppc_cpu_start ( void * args )
2005-04-16 15:20:36 -07:00
{
2008-12-16 09:36:25 +09:00
unsigned int counters = op_model_sh7750_ops . num_counters ;
int i ;
2005-04-16 15:20:36 -07:00
2008-12-16 09:36:25 +09:00
for ( i = 0 ; i < counters ; i + + )
__raw_writew ( regcache [ i ] . ctrl , PMCR ( i ) ) ;
2005-04-16 15:20:36 -07:00
}
2008-12-16 09:36:25 +09:00
static void sh7750_ppc_cpu_stop ( void * args )
2005-04-16 15:20:36 -07:00
{
2008-12-16 09:36:25 +09:00
unsigned int counters = op_model_sh7750_ops . num_counters ;
int i ;
2005-04-16 15:20:36 -07:00
2008-12-16 09:36:25 +09:00
/* Disable the counters */
for ( i = 0 ; i < counters ; i + + )
__raw_writew ( __raw_readw ( PMCR ( i ) ) & ~ PMCR_PMEN , PMCR ( i ) ) ;
}
2005-04-16 15:20:36 -07:00
2008-12-16 09:36:25 +09:00
static inline void sh7750_ppc_reset ( void )
{
unsigned int counters = op_model_sh7750_ops . num_counters ;
int i ;
2005-04-16 15:20:36 -07:00
/* Clear the counters */
2008-12-16 09:36:25 +09:00
for ( i = 0 ; i < counters ; i + + )
__raw_writew ( __raw_readw ( PMCR ( i ) ) | PMCR_PMCLR , PMCR ( i ) ) ;
}
2005-04-16 15:20:36 -07:00
2008-12-16 09:36:25 +09:00
static int sh7750_ppc_init ( void )
{
sh7750_ppc_reset ( ) ;
return register_timer_hook ( sh7750_timer_notify ) ;
2005-04-16 15:20:36 -07:00
}
2008-12-16 09:36:25 +09:00
static void sh7750_ppc_exit ( void )
2005-04-16 15:20:36 -07:00
{
2008-12-16 09:36:25 +09:00
unregister_timer_hook ( sh7750_timer_notify ) ;
sh7750_ppc_reset ( ) ;
2005-04-16 15:20:36 -07:00
}
2008-12-16 09:36:25 +09:00
struct op_sh_model op_model_sh7750_ops = {
. cpu_type = " sh/sh7750 " ,
. num_counters = NR_CNTRS ,
. reg_setup = sh7750_ppc_reg_setup ,
. cpu_setup = sh7750_ppc_cpu_setup ,
. cpu_start = sh7750_ppc_cpu_start ,
. cpu_stop = sh7750_ppc_cpu_stop ,
. init = sh7750_ppc_init ,
. exit = sh7750_ppc_exit ,
. create_files = sh7750_ppc_create_files ,
} ;