2005-04-17 02:20:36 +04:00
/*
2008-02-05 03:27:55 +03:00
* Freescale Embedded oprofile support , based on ppc64 oprofile support
2005-04-17 02:20:36 +04:00
* Copyright ( C ) 2004 Anton Blanchard < anton @ au . ibm . com > , IBM
*
2010-05-20 00:32:21 +04:00
* Copyright ( c ) 2004 , 2010 Freescale Semiconductor , Inc
2005-04-17 02:20:36 +04:00
*
* Author : Andy Fleming
2005-11-14 03:06:30 +03:00
* Maintainer : Kumar Gala < galak @ kernel . crashing . org >
2005-04-17 02:20:36 +04:00
*
* 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/smp.h>
# include <asm/ptrace.h>
# include <asm/processor.h>
# include <asm/cputable.h>
2008-02-05 03:27:55 +03:00
# include <asm/reg_fsl_emb.h>
2005-04-17 02:20:36 +04:00
# include <asm/page.h>
2005-10-19 08:53:32 +04:00
# include <asm/pmc.h>
2005-09-19 17:21:15 +04:00
# include <asm/oprofile_impl.h>
2005-04-17 02:20:36 +04:00
static unsigned long reset_value [ OP_MAX_COUNTER ] ;
static int num_counters ;
static int oprofile_running ;
2007-01-29 06:23:14 +03:00
static inline u32 get_pmlca ( int ctr )
{
u32 pmlca ;
switch ( ctr ) {
case 0 :
pmlca = mfpmr ( PMRN_PMLCA0 ) ;
break ;
case 1 :
pmlca = mfpmr ( PMRN_PMLCA1 ) ;
break ;
case 2 :
pmlca = mfpmr ( PMRN_PMLCA2 ) ;
break ;
case 3 :
pmlca = mfpmr ( PMRN_PMLCA3 ) ;
break ;
2013-06-06 00:22:09 +04:00
case 4 :
pmlca = mfpmr ( PMRN_PMLCA4 ) ;
break ;
case 5 :
pmlca = mfpmr ( PMRN_PMLCA5 ) ;
break ;
2007-01-29 06:23:14 +03:00
default :
panic ( " Bad ctr number \n " ) ;
}
return pmlca ;
}
static inline void set_pmlca ( int ctr , u32 pmlca )
{
switch ( ctr ) {
case 0 :
mtpmr ( PMRN_PMLCA0 , pmlca ) ;
break ;
case 1 :
mtpmr ( PMRN_PMLCA1 , pmlca ) ;
break ;
case 2 :
mtpmr ( PMRN_PMLCA2 , pmlca ) ;
break ;
case 3 :
mtpmr ( PMRN_PMLCA3 , pmlca ) ;
break ;
2013-06-06 00:22:09 +04:00
case 4 :
mtpmr ( PMRN_PMLCA4 , pmlca ) ;
break ;
case 5 :
mtpmr ( PMRN_PMLCA5 , pmlca ) ;
break ;
2007-01-29 06:23:14 +03:00
default :
panic ( " Bad ctr number \n " ) ;
}
}
static inline unsigned int ctr_read ( unsigned int i )
{
switch ( i ) {
case 0 :
return mfpmr ( PMRN_PMC0 ) ;
case 1 :
return mfpmr ( PMRN_PMC1 ) ;
case 2 :
return mfpmr ( PMRN_PMC2 ) ;
case 3 :
return mfpmr ( PMRN_PMC3 ) ;
2013-06-06 00:22:09 +04:00
case 4 :
return mfpmr ( PMRN_PMC4 ) ;
case 5 :
return mfpmr ( PMRN_PMC5 ) ;
2007-01-29 06:23:14 +03:00
default :
return 0 ;
}
}
static inline void ctr_write ( unsigned int i , unsigned int val )
{
switch ( i ) {
case 0 :
mtpmr ( PMRN_PMC0 , val ) ;
break ;
case 1 :
mtpmr ( PMRN_PMC1 , val ) ;
break ;
case 2 :
mtpmr ( PMRN_PMC2 , val ) ;
break ;
case 3 :
mtpmr ( PMRN_PMC3 , val ) ;
break ;
2013-06-06 00:22:09 +04:00
case 4 :
mtpmr ( PMRN_PMC4 , val ) ;
break ;
case 5 :
mtpmr ( PMRN_PMC5 , val ) ;
break ;
2007-01-29 06:23:14 +03:00
default :
break ;
}
}
2006-10-28 00:06:32 +04:00
static void init_pmc_stop ( int ctr )
2005-04-17 02:20:36 +04:00
{
2006-10-28 00:06:32 +04:00
u32 pmlca = ( PMLCA_FC | PMLCA_FCS | PMLCA_FCU |
PMLCA_FCM1 | PMLCA_FCM0 ) ;
u32 pmlcb = 0 ;
2005-04-17 02:20:36 +04:00
2006-10-28 00:06:32 +04:00
switch ( ctr ) {
2005-04-17 02:20:36 +04:00
case 0 :
2006-10-28 00:06:32 +04:00
mtpmr ( PMRN_PMLCA0 , pmlca ) ;
mtpmr ( PMRN_PMLCB0 , pmlcb ) ;
2005-04-17 02:20:36 +04:00
break ;
case 1 :
2006-10-28 00:06:32 +04:00
mtpmr ( PMRN_PMLCA1 , pmlca ) ;
mtpmr ( PMRN_PMLCB1 , pmlcb ) ;
2005-04-17 02:20:36 +04:00
break ;
case 2 :
2006-10-28 00:06:32 +04:00
mtpmr ( PMRN_PMLCA2 , pmlca ) ;
mtpmr ( PMRN_PMLCB2 , pmlcb ) ;
2005-04-17 02:20:36 +04:00
break ;
case 3 :
2006-10-28 00:06:32 +04:00
mtpmr ( PMRN_PMLCA3 , pmlca ) ;
mtpmr ( PMRN_PMLCB3 , pmlcb ) ;
2005-04-17 02:20:36 +04:00
break ;
2013-06-06 00:22:09 +04:00
case 4 :
mtpmr ( PMRN_PMLCA4 , pmlca ) ;
mtpmr ( PMRN_PMLCB4 , pmlcb ) ;
break ;
case 5 :
mtpmr ( PMRN_PMLCA5 , pmlca ) ;
mtpmr ( PMRN_PMLCB5 , pmlcb ) ;
break ;
2005-04-17 02:20:36 +04:00
default :
2006-10-28 00:06:32 +04:00
panic ( " Bad ctr number! \n " ) ;
2005-04-17 02:20:36 +04:00
}
}
2006-10-28 00:06:32 +04:00
static void set_pmc_event ( int ctr , int event )
{
u32 pmlca ;
pmlca = get_pmlca ( ctr ) ;
pmlca = ( pmlca & ~ PMLCA_EVENT_MASK ) |
( ( event < < PMLCA_EVENT_SHIFT ) &
PMLCA_EVENT_MASK ) ;
set_pmlca ( ctr , pmlca ) ;
}
static void set_pmc_user_kernel ( int ctr , int user , int kernel )
{
u32 pmlca ;
pmlca = get_pmlca ( ctr ) ;
if ( user )
pmlca & = ~ PMLCA_FCU ;
else
pmlca | = PMLCA_FCU ;
if ( kernel )
pmlca & = ~ PMLCA_FCS ;
else
pmlca | = PMLCA_FCS ;
set_pmlca ( ctr , pmlca ) ;
}
static void set_pmc_marked ( int ctr , int mark0 , int mark1 )
{
u32 pmlca = get_pmlca ( ctr ) ;
if ( mark0 )
pmlca & = ~ PMLCA_FCM0 ;
else
pmlca | = PMLCA_FCM0 ;
if ( mark1 )
pmlca & = ~ PMLCA_FCM1 ;
else
pmlca | = PMLCA_FCM1 ;
set_pmlca ( ctr , pmlca ) ;
}
static void pmc_start_ctr ( int ctr , int enable )
{
u32 pmlca = get_pmlca ( ctr ) ;
pmlca & = ~ PMLCA_FC ;
if ( enable )
pmlca | = PMLCA_CE ;
else
pmlca & = ~ PMLCA_CE ;
set_pmlca ( ctr , pmlca ) ;
}
static void pmc_start_ctrs ( int enable )
{
u32 pmgc0 = mfpmr ( PMRN_PMGC0 ) ;
pmgc0 & = ~ PMGC0_FAC ;
pmgc0 | = PMGC0_FCECE ;
if ( enable )
pmgc0 | = PMGC0_PMIE ;
else
pmgc0 & = ~ PMGC0_PMIE ;
mtpmr ( PMRN_PMGC0 , pmgc0 ) ;
}
static void pmc_stop_ctrs ( void )
{
u32 pmgc0 = mfpmr ( PMRN_PMGC0 ) ;
pmgc0 | = PMGC0_FAC ;
pmgc0 & = ~ ( PMGC0_PMIE | PMGC0_FCECE ) ;
mtpmr ( PMRN_PMGC0 , pmgc0 ) ;
}
2008-02-05 03:27:55 +03:00
static int fsl_emb_cpu_setup ( struct op_counter_config * ctr )
2006-10-28 00:06:32 +04:00
{
int i ;
/* freeze all counters */
pmc_stop_ctrs ( ) ;
for ( i = 0 ; i < num_counters ; i + + ) {
init_pmc_stop ( i ) ;
set_pmc_event ( i , ctr [ i ] . event ) ;
set_pmc_user_kernel ( i , ctr [ i ] . user , ctr [ i ] . kernel ) ;
}
2007-07-20 23:39:53 +04:00
return 0 ;
2006-10-28 00:06:32 +04:00
}
2005-04-17 02:20:36 +04:00
2008-02-05 03:27:55 +03:00
static int fsl_emb_reg_setup ( struct op_counter_config * ctr ,
2005-04-17 02:20:36 +04:00
struct op_system_config * sys ,
int num_ctrs )
{
int i ;
num_counters = num_ctrs ;
/* Our counters count up, and "count" refers to
* how much before the next interrupt , and we interrupt
* on overflow . So we calculate the starting value
* which will give us " count " until overflow .
* Then we set the events on the enabled counters */
2006-10-28 00:06:32 +04:00
for ( i = 0 ; i < num_counters ; + + i )
2005-04-17 02:20:36 +04:00
reset_value [ i ] = 0x80000000UL - ctr [ i ] . count ;
2007-07-20 23:39:53 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2008-02-05 03:27:55 +03:00
static int fsl_emb_start ( struct op_counter_config * ctr )
2005-04-17 02:20:36 +04:00
{
int i ;
mtmsr ( mfmsr ( ) | MSR_PMM ) ;
for ( i = 0 ; i < num_counters ; + + i ) {
if ( ctr [ i ] . enabled ) {
ctr_write ( i , reset_value [ i ] ) ;
2006-10-28 00:06:32 +04:00
/* Set each enabled counter to only
* count when the Mark bit is * not * set */
2005-04-17 02:20:36 +04:00
set_pmc_marked ( i , 1 , 0 ) ;
pmc_start_ctr ( i , 1 ) ;
} else {
ctr_write ( i , 0 ) ;
/* Set the ctr to be stopped */
pmc_start_ctr ( i , 0 ) ;
}
}
/* Clear the freeze bit, and enable the interrupt.
* The counters won ' t actually start until the rfi clears
* the PMM bit */
pmc_start_ctrs ( 1 ) ;
oprofile_running = 1 ;
pr_debug ( " start on cpu %d, pmgc0 %x \n " , smp_processor_id ( ) ,
mfpmr ( PMRN_PMGC0 ) ) ;
2007-07-20 23:39:53 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2008-02-05 03:27:55 +03:00
static void fsl_emb_stop ( void )
2005-04-17 02:20:36 +04:00
{
/* freeze counters */
pmc_stop_ctrs ( ) ;
oprofile_running = 0 ;
pr_debug ( " stop on cpu %d, pmgc0 %x \n " , smp_processor_id ( ) ,
mfpmr ( PMRN_PMGC0 ) ) ;
mb ( ) ;
}
2008-02-05 03:27:55 +03:00
static void fsl_emb_handle_interrupt ( struct pt_regs * regs ,
2005-04-17 02:20:36 +04:00
struct op_counter_config * ctr )
{
unsigned long pc ;
int is_kernel ;
int val ;
int i ;
pc = regs - > nip ;
2006-03-27 05:03:17 +04:00
is_kernel = is_kernel_addr ( pc ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < num_counters ; + + i ) {
val = ctr_read ( i ) ;
if ( val < 0 ) {
if ( oprofile_running & & ctr [ i ] . enabled ) {
2006-03-27 05:03:17 +04:00
oprofile_add_ext_sample ( pc , regs , i , is_kernel ) ;
2005-04-17 02:20:36 +04:00
ctr_write ( i , reset_value [ i ] ) ;
} else {
ctr_write ( i , 0 ) ;
}
}
}
/* The freeze bit was set by the interrupt. */
2010-05-20 00:32:21 +04:00
/* Clear the freeze bit, and reenable the interrupt. The
* counters won ' t actually start until the rfi clears the PMM
* bit . The PMM bit should not be set until after the interrupt
* is cleared to avoid it getting lost in some hypervisor
* environments .
*/
mtmsr ( mfmsr ( ) | MSR_PMM ) ;
2005-04-17 02:20:36 +04:00
pmc_start_ctrs ( 1 ) ;
}
2008-02-05 03:27:55 +03:00
struct op_powerpc_model op_model_fsl_emb = {
. reg_setup = fsl_emb_reg_setup ,
. cpu_setup = fsl_emb_cpu_setup ,
. start = fsl_emb_start ,
. stop = fsl_emb_stop ,
. handle_interrupt = fsl_emb_handle_interrupt ,
2005-04-17 02:20:36 +04:00
} ;