2006-10-17 19:21:48 -07:00
/**
* @ file init . c
*
* @ remark Copyright 2002 OProfile authors
* @ remark Read the file COPYING
*
* @ author John Levon < levon @ movementarian . org >
*/
# include <linux/kernel.h>
# include <linux/oprofile.h>
# include <linux/errno.h>
# include <linux/init.h>
2008-11-25 22:29:24 -08:00
# ifdef CONFIG_SPARC64
2008-11-28 02:27:42 -08:00
# include <asm/hypervisor.h>
2008-11-25 22:29:24 -08:00
# include <asm/spitfire.h>
# include <asm/cpudata.h>
# include <asm/irq.h>
2009-01-21 21:30:23 -08:00
# include <asm/pcr.h>
2008-11-25 22:29:24 -08:00
static int nmi_enabled ;
2008-11-28 02:27:42 -08:00
/* In order to commonize as much of the implementation as
* possible , we use PICH as our counter . Mostly this is
* to accomodate Niagara - 1 which can only count insn cycles
* in PICH .
*/
2008-11-25 22:29:24 -08:00
static u64 picl_value ( void )
{
u32 delta = local_cpu_data ( ) . clock_tick / HZ ;
2008-11-28 02:27:42 -08:00
return ( ( u64 ) ( ( 0 - delta ) & 0xffffffff ) ) < < 32 ;
2008-11-25 22:29:24 -08:00
}
2008-11-28 02:27:42 -08:00
# define PCR_SUN4U_ENABLE (PCR_PIC_PRIV | PCR_STRACE | PCR_UTRACE)
# define PCR_N2_ENABLE (PCR_PIC_PRIV | PCR_STRACE | PCR_UTRACE | \
PCR_N2_TOE_OV1 | \
( 2 < < PCR_N2_SL1_SHIFT ) | \
( 0xff < < PCR_N2_MASK1_SHIFT ) )
2009-01-21 21:30:23 -08:00
static u64 pcr_enable ;
2008-11-25 22:29:24 -08:00
static void nmi_handler ( struct pt_regs * regs )
{
2008-11-28 02:27:42 -08:00
pcr_ops - > write ( PCR_PIC_PRIV ) ;
2008-11-25 22:29:24 -08:00
if ( nmi_enabled ) {
oprofile_add_sample ( regs , 0 ) ;
write_pic ( picl_value ( ) ) ;
2008-11-28 02:27:42 -08:00
pcr_ops - > write ( pcr_enable ) ;
2008-11-25 22:29:24 -08:00
}
}
/* We count "clock cycle" events in the lower 32-bit PIC.
* Then configure it such that it overflows every HZ , and thus
* generates a level 15 interrupt at that frequency .
*/
static void cpu_nmi_start ( void * _unused )
{
2008-11-28 02:27:42 -08:00
pcr_ops - > write ( PCR_PIC_PRIV ) ;
2008-11-25 22:29:24 -08:00
write_pic ( picl_value ( ) ) ;
2008-11-28 02:27:42 -08:00
pcr_ops - > write ( pcr_enable ) ;
2008-11-25 22:29:24 -08:00
}
static void cpu_nmi_stop ( void * _unused )
{
2008-11-28 02:27:42 -08:00
pcr_ops - > write ( PCR_PIC_PRIV ) ;
2008-11-25 22:29:24 -08:00
}
static int nmi_start ( void )
{
int err = register_perfctr_intr ( nmi_handler ) ;
if ( ! err ) {
nmi_enabled = 1 ;
wmb ( ) ;
err = on_each_cpu ( cpu_nmi_start , NULL , 1 ) ;
if ( err ) {
nmi_enabled = 0 ;
wmb ( ) ;
on_each_cpu ( cpu_nmi_stop , NULL , 1 ) ;
release_perfctr_intr ( nmi_handler ) ;
}
}
return err ;
}
static void nmi_stop ( void )
{
nmi_enabled = 0 ;
wmb ( ) ;
on_each_cpu ( cpu_nmi_stop , NULL , 1 ) ;
release_perfctr_intr ( nmi_handler ) ;
synchronize_sched ( ) ;
}
static int oprofile_nmi_init ( struct oprofile_operations * ops )
{
2008-11-28 02:27:42 -08:00
switch ( tlb_type ) {
case hypervisor :
pcr_enable = PCR_N2_ENABLE ;
break ;
case cheetah :
case cheetah_plus :
2009-01-21 21:30:23 -08:00
pcr_enable = PCR_SUN4U_ENABLE ;
2008-11-28 02:27:42 -08:00
break ;
default :
2008-11-25 22:29:24 -08:00
return - ENODEV ;
2008-11-28 02:27:42 -08:00
}
2008-11-25 22:29:24 -08:00
ops - > create_files = NULL ;
ops - > setup = NULL ;
ops - > shutdown = NULL ;
ops - > start = nmi_start ;
ops - > stop = nmi_stop ;
ops - > cpu_type = " timer " ;
printk ( KERN_INFO " oprofile: Using perfctr based NMI timer interrupt. \n " ) ;
return 0 ;
}
# endif
2008-09-05 17:12:36 +02:00
int __init oprofile_arch_init ( struct oprofile_operations * ops )
2006-10-17 19:21:48 -07:00
{
2008-11-25 22:29:24 -08:00
int ret = - ENODEV ;
# ifdef CONFIG_SPARC64
ret = oprofile_nmi_init ( ops ) ;
if ( ! ret )
return ret ;
# endif
return ret ;
2006-10-17 19:21:48 -07:00
}
void oprofile_arch_exit ( void )
{
}