2005-04-16 15:20:36 -07: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 .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
2007-03-09 15:59:56 +00:00
*
* Copyright ( C ) 2001 , 2002 , 2003 Broadcom Corporation
* Copyright ( C ) 2007 Ralf Baechle < ralf @ linux - mips . org >
* Copyright ( C ) 2007 MIPS Technologies , Inc .
* written by Ralf Baechle < ralf @ linux - mips . org >
2005-04-16 15:20:36 -07:00
*/
2007-03-09 15:59:56 +00:00
# undef DEBUG
2005-04-16 15:20:36 -07:00
2007-03-09 15:59:56 +00:00
# include <linux/device.h>
2005-04-16 15:20:36 -07:00
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/types.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/slab.h>
2008-05-15 09:10:50 -06:00
# include <linux/smp_lock.h>
2005-04-16 15:20:36 -07:00
# include <linux/vmalloc.h>
# include <linux/fs.h>
# include <linux/errno.h>
2005-02-03 13:37:41 +00:00
# include <linux/wait.h>
2005-04-16 15:20:36 -07:00
# include <asm/io.h>
# include <asm/sibyte/sb1250.h>
2007-03-29 11:39:56 -07:00
# if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)
# include <asm/sibyte/bcm1480_regs.h>
# include <asm/sibyte/bcm1480_scd.h>
# include <asm/sibyte/bcm1480_int.h>
# elif defined(CONFIG_SIBYTE_SB1250) || defined(CONFIG_SIBYTE_BCM112X)
2005-04-16 15:20:36 -07:00
# include <asm/sibyte/sb1250_regs.h>
# include <asm/sibyte/sb1250_scd.h>
# include <asm/sibyte/sb1250_int.h>
2007-03-29 11:39:56 -07:00
# else
# error invalid SiByte UART configuation
# endif
# if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)
# undef K_INT_TRACE_FREEZE
# define K_INT_TRACE_FREEZE K_BCM1480_INT_TRACE_FREEZE
# undef K_INT_PERF_CNT
# define K_INT_PERF_CNT K_BCM1480_INT_PERF_CNT
# endif
2007-03-09 15:59:56 +00:00
# include <asm/system.h>
# include <asm/uaccess.h>
2005-04-16 15:20:36 -07:00
2007-03-09 15:59:56 +00:00
# define SBPROF_TB_MAJOR 240
typedef u64 tb_sample_t [ 6 * 256 ] ;
enum open_status {
SB_CLOSED ,
SB_OPENING ,
SB_OPEN
} ;
struct sbprof_tb {
wait_queue_head_t tb_sync ;
wait_queue_head_t tb_read ;
struct mutex lock ;
enum open_status open ;
tb_sample_t * sbprof_tbbuf ;
int next_tb_sample ;
volatile int tb_enable ;
volatile int tb_armed ;
} ;
2005-04-16 15:20:36 -07:00
static struct sbprof_tb sbp ;
2007-03-09 15:59:56 +00:00
# define MAX_SAMPLE_BYTES (24*1024*1024)
# define MAX_TBSAMPLE_BYTES (12*1024*1024)
# define MAX_SAMPLES (MAX_SAMPLE_BYTES / sizeof(u_int32_t))
# define TB_SAMPLE_SIZE (sizeof(tb_sample_t))
# define MAX_TB_SAMPLES (MAX_TBSAMPLE_BYTES / TB_SAMPLE_SIZE)
/* ioctls */
# define SBPROF_ZBSTART _IOW('s', 0, int)
# define SBPROF_ZBSTOP _IOW('s', 1, int)
# define SBPROF_ZBWAITFULL _IOW('s', 2, int)
/*
* Routines for using 40 - bit SCD cycle counter
*
* Client responsible for either handling interrupts or making sure
* the cycles counter never saturates , e . g . , by doing
* zclk_timer_init ( 0 ) at least every 2 ^ 40 - 1 ZCLKs .
*/
/*
* Configures SCD counter 0 to count ZCLKs starting from val ;
* Configures SCD counters1 , 2 , 3 to count nothing .
* Must not be called while gathering ZBbus profiles .
*/
# define zclk_timer_init(val) \
__asm__ __volatile__ ( " .set push; " \
" .set mips64; " \
" la $8, 0xb00204c0; " /* SCD perf_cnt_cfg */ \
" sd %0, 0x10($8); " /* write val to counter0 */ \
" sd %1, 0($8); " /* config counter0 for zclks*/ \
" .set pop " \
: /* no outputs */ \
/* enable, counter0 */ \
: /* inputs */ " r " ( val ) , " r " ( ( 1ULL < < 33 ) | 1ULL ) \
: /* modifies */ " $8 " )
/* Reads SCD counter 0 and puts result in value
unsigned long long val ; */
# define zclk_get(val) \
__asm__ __volatile__ ( " .set push; " \
" .set mips64; " \
" la $8, 0xb00204c0; " /* SCD perf_cnt_cfg */ \
" ld %0, 0x10($8); " /* write val to counter0 */ \
" .set pop " \
: /* outputs */ " =r " ( val ) \
: /* inputs */ \
: /* modifies */ " $8 " )
2007-03-29 11:39:56 -07:00
# define DEVNAME "sb_tbprof"
2007-03-09 15:59:56 +00:00
2005-04-16 15:20:36 -07:00
# define TB_FULL (sbp.next_tb_sample == MAX_TB_SAMPLES)
2007-03-09 15:59:56 +00:00
/*
2005-04-16 15:20:36 -07:00
* Support for ZBbus sampling using the trace buffer
*
* We use the SCD performance counter interrupt , caused by a Zclk counter
* overflow , to trigger the start of tracing .
*
* We set the trace buffer to sample everything and freeze on
* overflow .
*
* We map the interrupt for trace_buffer_freeze to handle it on CPU 0.
2007-03-29 11:39:56 -07:00
*
2007-03-09 15:59:56 +00:00
*/
2005-04-16 15:20:36 -07:00
2007-03-09 15:59:56 +00:00
static u64 tb_period ;
2005-04-16 15:20:36 -07:00
static void arm_tb ( void )
{
2007-03-09 15:59:56 +00:00
u64 scdperfcnt ;
u64 next = ( 1ULL < < 40 ) - tb_period ;
u64 tb_options = M_SCD_TRACE_CFG_FREEZE_FULL ;
/*
2007-03-29 11:39:56 -07:00
* Generate an SCD_PERFCNT interrupt in TB_PERIOD Zclks to
* trigger start of trace . XXX vary sampling period
2007-03-09 15:59:56 +00:00
*/
2005-02-22 21:51:30 +00:00
__raw_writeq ( 0 , IOADDR ( A_SCD_PERF_CNT_1 ) ) ;
scdperfcnt = __raw_readq ( IOADDR ( A_SCD_PERF_CNT_CFG ) ) ;
2007-03-09 15:59:56 +00:00
/*
2007-03-29 11:39:56 -07:00
* Unfortunately , in Pass 2 we must clear all counters to knock down
* a previous interrupt request . This means that bus profiling
* requires ALL of the SCD perf counters .
2007-03-09 15:59:56 +00:00
*/
2007-03-29 11:39:56 -07:00
# if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)
__raw_writeq ( ( scdperfcnt & ~ M_SPC_CFG_SRC1 ) |
/* keep counters 0,2,3,4,5,6,7 as is */
V_SPC_CFG_SRC1 ( 1 ) , /* counter 1 counts cycles */
IOADDR ( A_BCM1480_SCD_PERF_CNT_CFG0 ) ) ;
__raw_writeq (
M_SPC_CFG_ENABLE | /* enable counting */
M_SPC_CFG_CLEAR | /* clear all counters */
V_SPC_CFG_SRC1 ( 1 ) , /* counter 1 counts cycles */
IOADDR ( A_BCM1480_SCD_PERF_CNT_CFG1 ) ) ;
# else
2005-02-22 21:51:30 +00:00
__raw_writeq ( ( scdperfcnt & ~ M_SPC_CFG_SRC1 ) |
2007-03-09 15:59:56 +00:00
/* keep counters 0,2,3 as is */
M_SPC_CFG_ENABLE | /* enable counting */
M_SPC_CFG_CLEAR | /* clear all counters */
V_SPC_CFG_SRC1 ( 1 ) , /* counter 1 counts cycles */
2005-02-22 21:51:30 +00:00
IOADDR ( A_SCD_PERF_CNT_CFG ) ) ;
2007-03-29 11:39:56 -07:00
# endif
2005-02-22 21:51:30 +00:00
__raw_writeq ( next , IOADDR ( A_SCD_PERF_CNT_1 ) ) ;
2005-04-16 15:20:36 -07:00
/* Reset the trace buffer */
2005-02-22 21:51:30 +00:00
__raw_writeq ( M_SCD_TRACE_CFG_RESET , IOADDR ( A_SCD_TRACE_CFG ) ) ;
2005-04-16 15:20:36 -07:00
#if 0 && defined(M_SCD_TRACE_CFG_FORCECNT)
/* XXXKW may want to expose control to the data-collector */
tb_options | = M_SCD_TRACE_CFG_FORCECNT ;
# endif
2005-02-22 21:51:30 +00:00
__raw_writeq ( tb_options , IOADDR ( A_SCD_TRACE_CFG ) ) ;
2005-04-16 15:20:36 -07:00
sbp . tb_armed = 1 ;
}
2006-10-15 09:19:58 +01:00
static irqreturn_t sbprof_tb_intr ( int irq , void * dev_id )
2005-04-16 15:20:36 -07:00
{
int i ;
2007-03-09 15:59:56 +00:00
pr_debug ( DEVNAME " : tb_intr \n " ) ;
2005-04-16 15:20:36 -07:00
if ( sbp . next_tb_sample < MAX_TB_SAMPLES ) {
/* XXX should use XKPHYS to make writes bypass L2 */
2007-03-09 15:59:56 +00:00
u64 * p = sbp . sbprof_tbbuf [ sbp . next_tb_sample + + ] ;
2005-04-16 15:20:36 -07:00
/* Read out trace */
2005-02-22 21:51:30 +00:00
__raw_writeq ( M_SCD_TRACE_CFG_START_READ ,
IOADDR ( A_SCD_TRACE_CFG ) ) ;
2005-04-16 15:20:36 -07:00
__asm__ __volatile__ ( " sync " : : : " memory " ) ;
/* Loop runs backwards because bundles are read out in reverse order */
for ( i = 256 * 6 ; i > 0 ; i - = 6 ) {
2007-03-09 15:59:56 +00:00
/* Subscripts decrease to put bundle in the order */
/* t0 lo, t0 hi, t1 lo, t1 hi, t2 lo, t2 hi */
2005-02-22 21:51:30 +00:00
p [ i - 1 ] = __raw_readq ( IOADDR ( A_SCD_TRACE_READ ) ) ;
2007-03-29 11:39:56 -07:00
/* read t2 hi */
2005-02-22 21:51:30 +00:00
p [ i - 2 ] = __raw_readq ( IOADDR ( A_SCD_TRACE_READ ) ) ;
2007-03-29 11:39:56 -07:00
/* read t2 lo */
2005-02-22 21:51:30 +00:00
p [ i - 3 ] = __raw_readq ( IOADDR ( A_SCD_TRACE_READ ) ) ;
2007-03-29 11:39:56 -07:00
/* read t1 hi */
2005-02-22 21:51:30 +00:00
p [ i - 4 ] = __raw_readq ( IOADDR ( A_SCD_TRACE_READ ) ) ;
2007-03-29 11:39:56 -07:00
/* read t1 lo */
2005-02-22 21:51:30 +00:00
p [ i - 5 ] = __raw_readq ( IOADDR ( A_SCD_TRACE_READ ) ) ;
2007-03-29 11:39:56 -07:00
/* read t0 hi */
2005-02-22 21:51:30 +00:00
p [ i - 6 ] = __raw_readq ( IOADDR ( A_SCD_TRACE_READ ) ) ;
2007-03-29 11:39:56 -07:00
/* read t0 lo */
2005-04-16 15:20:36 -07:00
}
if ( ! sbp . tb_enable ) {
2007-03-09 15:59:56 +00:00
pr_debug ( DEVNAME " : tb_intr shutdown \n " ) ;
2005-02-22 21:51:30 +00:00
__raw_writeq ( M_SCD_TRACE_CFG_RESET ,
IOADDR ( A_SCD_TRACE_CFG ) ) ;
2005-04-16 15:20:36 -07:00
sbp . tb_armed = 0 ;
2007-03-29 11:39:56 -07:00
wake_up_interruptible ( & sbp . tb_sync ) ;
2005-04-16 15:20:36 -07:00
} else {
2007-03-29 11:39:56 -07:00
/* knock down current interrupt and get another one later */
arm_tb ( ) ;
2005-04-16 15:20:36 -07:00
}
} else {
/* No more trace buffer samples */
2007-03-09 15:59:56 +00:00
pr_debug ( DEVNAME " : tb_intr full \n " ) ;
2005-02-22 21:51:30 +00:00
__raw_writeq ( M_SCD_TRACE_CFG_RESET , IOADDR ( A_SCD_TRACE_CFG ) ) ;
2005-04-16 15:20:36 -07:00
sbp . tb_armed = 0 ;
2007-03-29 11:39:56 -07:00
if ( ! sbp . tb_enable )
wake_up_interruptible ( & sbp . tb_sync ) ;
wake_up_interruptible ( & sbp . tb_read ) ;
2005-04-16 15:20:36 -07:00
}
return IRQ_HANDLED ;
}
2006-10-15 09:19:58 +01:00
static irqreturn_t sbprof_pc_intr ( int irq , void * dev_id )
2005-04-16 15:20:36 -07:00
{
printk ( DEVNAME " : unexpected pc_intr " ) ;
return IRQ_NONE ;
}
2007-03-09 15:59:56 +00:00
/*
* Requires : Already called zclk_timer_init with a value that won ' t
* saturate 40 bits . No subsequent use of SCD performance counters
* or trace buffer .
*/
static int sbprof_zbprof_start ( struct file * filp )
2005-04-16 15:20:36 -07:00
{
2007-03-09 15:59:56 +00:00
u64 scdperfcnt ;
int err ;
2005-04-16 15:20:36 -07:00
2007-03-09 15:59:56 +00:00
if ( xchg ( & sbp . tb_enable , 1 ) )
2005-04-16 15:20:36 -07:00
return - EBUSY ;
2007-03-09 15:59:56 +00:00
pr_debug ( DEVNAME " : starting \n " ) ;
2005-04-16 15:20:36 -07:00
sbp . next_tb_sample = 0 ;
filp - > f_pos = 0 ;
2007-10-11 23:46:15 +01:00
err = request_irq ( K_INT_TRACE_FREEZE , sbprof_tb_intr , 0 ,
DEVNAME " trace freeze " , & sbp ) ;
2007-03-09 15:59:56 +00:00
if ( err )
2005-04-16 15:20:36 -07:00
return - EBUSY ;
2007-03-09 15:59:56 +00:00
2005-04-16 15:20:36 -07:00
/* Make sure there isn't a perf-cnt interrupt waiting */
2005-02-22 21:51:30 +00:00
scdperfcnt = __raw_readq ( IOADDR ( A_SCD_PERF_CNT_CFG ) ) ;
2005-04-16 15:20:36 -07:00
/* Disable and clear counters, override SRC_1 */
2005-02-22 21:51:30 +00:00
__raw_writeq ( ( scdperfcnt & ~ ( M_SPC_CFG_SRC1 | M_SPC_CFG_ENABLE ) ) |
M_SPC_CFG_ENABLE | M_SPC_CFG_CLEAR | V_SPC_CFG_SRC1 ( 1 ) ,
IOADDR ( A_SCD_PERF_CNT_CFG ) ) ;
2005-04-16 15:20:36 -07:00
2007-03-09 15:59:56 +00:00
/*
2007-03-29 11:39:56 -07:00
* We grab this interrupt to prevent others from trying to use
* it , even though we don ' t want to service the interrupts
* ( they only feed into the trace - on - interrupt mechanism )
2007-03-09 15:59:56 +00:00
*/
2007-03-29 11:39:56 -07:00
if ( request_irq ( K_INT_PERF_CNT , sbprof_pc_intr , 0 , DEVNAME " scd perfcnt " , & sbp ) ) {
free_irq ( K_INT_TRACE_FREEZE , & sbp ) ;
return - EBUSY ;
}
2007-03-09 15:59:56 +00:00
/*
2007-03-29 11:39:56 -07:00
* I need the core to mask these , but the interrupt mapper to
* pass them through . I am exploiting my knowledge that
* cp0_status masks out IP [ 5 ] . krw
2007-03-09 15:59:56 +00:00
*/
2007-03-29 11:39:56 -07:00
# if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)
__raw_writeq ( K_BCM1480_INT_MAP_I3 ,
IOADDR ( A_BCM1480_IMR_REGISTER ( 0 , R_BCM1480_IMR_INTERRUPT_MAP_BASE_L ) +
( ( K_BCM1480_INT_PERF_CNT & 0x3f ) < < 3 ) ) ) ;
# else
2005-02-22 21:51:30 +00:00
__raw_writeq ( K_INT_MAP_I3 ,
IOADDR ( A_IMR_REGISTER ( 0 , R_IMR_INTERRUPT_MAP_BASE ) +
( K_INT_PERF_CNT < < 3 ) ) ) ;
2007-03-29 11:39:56 -07:00
# endif
2005-04-16 15:20:36 -07:00
/* Initialize address traps */
2005-02-22 21:51:30 +00:00
__raw_writeq ( 0 , IOADDR ( A_ADDR_TRAP_UP_0 ) ) ;
__raw_writeq ( 0 , IOADDR ( A_ADDR_TRAP_UP_1 ) ) ;
__raw_writeq ( 0 , IOADDR ( A_ADDR_TRAP_UP_2 ) ) ;
__raw_writeq ( 0 , IOADDR ( A_ADDR_TRAP_UP_3 ) ) ;
2005-04-16 15:20:36 -07:00
2005-02-22 21:51:30 +00:00
__raw_writeq ( 0 , IOADDR ( A_ADDR_TRAP_DOWN_0 ) ) ;
__raw_writeq ( 0 , IOADDR ( A_ADDR_TRAP_DOWN_1 ) ) ;
__raw_writeq ( 0 , IOADDR ( A_ADDR_TRAP_DOWN_2 ) ) ;
__raw_writeq ( 0 , IOADDR ( A_ADDR_TRAP_DOWN_3 ) ) ;
2005-04-16 15:20:36 -07:00
2005-02-22 21:51:30 +00:00
__raw_writeq ( 0 , IOADDR ( A_ADDR_TRAP_CFG_0 ) ) ;
__raw_writeq ( 0 , IOADDR ( A_ADDR_TRAP_CFG_1 ) ) ;
__raw_writeq ( 0 , IOADDR ( A_ADDR_TRAP_CFG_2 ) ) ;
__raw_writeq ( 0 , IOADDR ( A_ADDR_TRAP_CFG_3 ) ) ;
2005-04-16 15:20:36 -07:00
/* Initialize Trace Event 0-7 */
2007-03-29 11:39:56 -07:00
/* when interrupt */
2005-02-22 21:51:30 +00:00
__raw_writeq ( M_SCD_TREVT_INTERRUPT , IOADDR ( A_SCD_TRACE_EVENT_0 ) ) ;
__raw_writeq ( 0 , IOADDR ( A_SCD_TRACE_EVENT_1 ) ) ;
__raw_writeq ( 0 , IOADDR ( A_SCD_TRACE_EVENT_2 ) ) ;
__raw_writeq ( 0 , IOADDR ( A_SCD_TRACE_EVENT_3 ) ) ;
__raw_writeq ( 0 , IOADDR ( A_SCD_TRACE_EVENT_4 ) ) ;
__raw_writeq ( 0 , IOADDR ( A_SCD_TRACE_EVENT_5 ) ) ;
__raw_writeq ( 0 , IOADDR ( A_SCD_TRACE_EVENT_6 ) ) ;
__raw_writeq ( 0 , IOADDR ( A_SCD_TRACE_EVENT_7 ) ) ;
2005-04-16 15:20:36 -07:00
/* Initialize Trace Sequence 0-7 */
2007-03-09 15:59:56 +00:00
/* Start on event 0 (interrupt) */
2005-02-22 21:51:30 +00:00
__raw_writeq ( V_SCD_TRSEQ_FUNC_START | 0x0fff ,
IOADDR ( A_SCD_TRACE_SEQUENCE_0 ) ) ;
2007-03-09 15:59:56 +00:00
/* dsamp when d used | asamp when a used */
2005-02-22 21:51:30 +00:00
__raw_writeq ( M_SCD_TRSEQ_ASAMPLE | M_SCD_TRSEQ_DSAMPLE |
K_SCD_TRSEQ_TRIGGER_ALL ,
IOADDR ( A_SCD_TRACE_SEQUENCE_1 ) ) ;
__raw_writeq ( 0 , IOADDR ( A_SCD_TRACE_SEQUENCE_2 ) ) ;
__raw_writeq ( 0 , IOADDR ( A_SCD_TRACE_SEQUENCE_3 ) ) ;
__raw_writeq ( 0 , IOADDR ( A_SCD_TRACE_SEQUENCE_4 ) ) ;
__raw_writeq ( 0 , IOADDR ( A_SCD_TRACE_SEQUENCE_5 ) ) ;
__raw_writeq ( 0 , IOADDR ( A_SCD_TRACE_SEQUENCE_6 ) ) ;
__raw_writeq ( 0 , IOADDR ( A_SCD_TRACE_SEQUENCE_7 ) ) ;
2005-04-16 15:20:36 -07:00
/* Now indicate the PERF_CNT interrupt as a trace-relevant interrupt */
2007-03-29 11:39:56 -07:00
# if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)
__raw_writeq ( 1ULL < < ( K_BCM1480_INT_PERF_CNT & 0x3f ) ,
IOADDR ( A_BCM1480_IMR_REGISTER ( 0 , R_BCM1480_IMR_INTERRUPT_TRACE_L ) ) ) ;
# else
2005-02-22 21:51:30 +00:00
__raw_writeq ( 1ULL < < K_INT_PERF_CNT ,
IOADDR ( A_IMR_REGISTER ( 0 , R_IMR_INTERRUPT_TRACE ) ) ) ;
2007-03-29 11:39:56 -07:00
# endif
2005-04-16 15:20:36 -07:00
arm_tb ( ) ;
2007-03-09 15:59:56 +00:00
pr_debug ( DEVNAME " : done starting \n " ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2007-03-09 15:59:56 +00:00
static int sbprof_zbprof_stop ( void )
2005-04-16 15:20:36 -07:00
{
2007-03-29 11:39:56 -07:00
int err = 0 ;
2007-03-09 15:59:56 +00:00
pr_debug ( DEVNAME " : stopping \n " ) ;
2005-04-16 15:20:36 -07:00
if ( sbp . tb_enable ) {
2007-03-09 15:59:56 +00:00
/*
* XXXKW there is a window here where the intr handler may run ,
* see the disable , and do the wake_up before this sleep
* happens .
*/
pr_debug ( DEVNAME " : wait for disarm \n " ) ;
err = wait_event_interruptible ( sbp . tb_sync , ! sbp . tb_armed ) ;
pr_debug ( DEVNAME " : disarm complete, stat %d \n " , err ) ;
if ( err )
return err ;
2005-04-16 15:20:36 -07:00
sbp . tb_enable = 0 ;
free_irq ( K_INT_TRACE_FREEZE , & sbp ) ;
free_irq ( K_INT_PERF_CNT , & sbp ) ;
}
2007-03-09 15:59:56 +00:00
pr_debug ( DEVNAME " : done stopping \n " ) ;
2005-04-16 15:20:36 -07:00
2007-03-29 11:39:56 -07:00
return err ;
2005-04-16 15:20:36 -07:00
}
static int sbprof_tb_open ( struct inode * inode , struct file * filp )
{
int minor ;
2008-05-15 09:10:50 -06:00
int err = 0 ;
2005-04-16 15:20:36 -07:00
2008-05-15 09:10:50 -06:00
lock_kernel ( ) ;
2005-04-16 15:20:36 -07:00
minor = iminor ( inode ) ;
2008-05-15 09:10:50 -06:00
if ( minor ! = 0 ) {
err = - ENODEV ;
goto out ;
}
2007-03-09 15:59:56 +00:00
2008-05-15 09:10:50 -06:00
if ( xchg ( & sbp . open , SB_OPENING ) ! = SB_CLOSED ) {
err = - EBUSY ;
goto out ;
}
2005-04-16 15:20:36 -07:00
memset ( & sbp , 0 , sizeof ( struct sbprof_tb ) ) ;
sbp . sbprof_tbbuf = vmalloc ( MAX_TBSAMPLE_BYTES ) ;
2008-05-15 09:10:50 -06:00
if ( ! sbp . sbprof_tbbuf ) {
err = - ENOMEM ;
goto out ;
}
2005-04-16 15:20:36 -07:00
memset ( sbp . sbprof_tbbuf , 0 , MAX_TBSAMPLE_BYTES ) ;
init_waitqueue_head ( & sbp . tb_sync ) ;
init_waitqueue_head ( & sbp . tb_read ) ;
2007-03-09 15:59:56 +00:00
mutex_init ( & sbp . lock ) ;
sbp . open = SB_OPEN ;
2005-04-16 15:20:36 -07:00
2008-05-15 09:10:50 -06:00
out :
unlock_kernel ( ) ;
return err ;
2005-04-16 15:20:36 -07:00
}
static int sbprof_tb_release ( struct inode * inode , struct file * filp )
{
2007-03-29 11:39:56 -07:00
int minor ;
2005-04-16 15:20:36 -07:00
2007-03-29 11:39:56 -07:00
minor = iminor ( inode ) ;
2007-03-09 15:59:56 +00:00
if ( minor ! = 0 | | ! sbp . open )
2005-04-16 15:20:36 -07:00
return - ENODEV ;
2007-03-09 15:59:56 +00:00
mutex_lock ( & sbp . lock ) ;
if ( sbp . tb_armed | | sbp . tb_enable )
2005-04-16 15:20:36 -07:00
sbprof_zbprof_stop ( ) ;
vfree ( sbp . sbprof_tbbuf ) ;
sbp . open = 0 ;
2007-03-09 15:59:56 +00:00
mutex_unlock ( & sbp . lock ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
static ssize_t sbprof_tb_read ( struct file * filp , char * buf ,
size_t size , loff_t * offp )
{
int cur_sample , sample_off , cur_count , sample_left ;
2007-03-09 15:59:56 +00:00
char * src ;
2007-03-29 11:39:56 -07:00
int count = 0 ;
char * dest = buf ;
long cur_off = * offp ;
2007-03-09 15:59:56 +00:00
if ( ! access_ok ( VERIFY_WRITE , buf , size ) )
return - EFAULT ;
mutex_lock ( & sbp . lock ) ;
2005-04-16 15:20:36 -07:00
count = 0 ;
cur_sample = cur_off / TB_SAMPLE_SIZE ;
sample_off = cur_off % TB_SAMPLE_SIZE ;
sample_left = TB_SAMPLE_SIZE - sample_off ;
2007-03-09 15:59:56 +00:00
2005-04-16 15:20:36 -07:00
while ( size & & ( cur_sample < sbp . next_tb_sample ) ) {
2007-03-09 15:59:56 +00:00
int err ;
2005-04-16 15:20:36 -07:00
cur_count = size < sample_left ? size : sample_left ;
src = ( char * ) ( ( ( long ) sbp . sbprof_tbbuf [ cur_sample ] ) + sample_off ) ;
2007-03-09 15:59:56 +00:00
err = __copy_to_user ( dest , src , cur_count ) ;
if ( err ) {
* offp = cur_off + cur_count - err ;
mutex_unlock ( & sbp . lock ) ;
return err ;
}
pr_debug ( DEVNAME " : read from sample %d, %d bytes \n " ,
cur_sample , cur_count ) ;
2005-04-16 15:20:36 -07:00
size - = cur_count ;
sample_left - = cur_count ;
if ( ! sample_left ) {
cur_sample + + ;
sample_off = 0 ;
sample_left = TB_SAMPLE_SIZE ;
} else {
sample_off + = cur_count ;
}
cur_off + = cur_count ;
dest + = cur_count ;
count + = cur_count ;
}
* offp = cur_off ;
2007-03-09 15:59:56 +00:00
mutex_unlock ( & sbp . lock ) ;
2005-04-16 15:20:36 -07:00
return count ;
}
2007-03-29 11:39:56 -07:00
static long sbprof_tb_ioctl ( struct file * filp ,
unsigned int command ,
unsigned long arg )
2005-04-16 15:20:36 -07:00
{
2007-03-29 11:39:56 -07:00
int err = 0 ;
2005-04-16 15:20:36 -07:00
switch ( command ) {
case SBPROF_ZBSTART :
2007-03-09 15:59:56 +00:00
mutex_lock ( & sbp . lock ) ;
2007-03-29 11:39:56 -07:00
err = sbprof_zbprof_start ( filp ) ;
2007-03-09 15:59:56 +00:00
mutex_unlock ( & sbp . lock ) ;
2005-04-16 15:20:36 -07:00
break ;
2007-03-09 15:59:56 +00:00
2005-04-16 15:20:36 -07:00
case SBPROF_ZBSTOP :
2007-03-09 15:59:56 +00:00
mutex_lock ( & sbp . lock ) ;
2007-03-29 11:39:56 -07:00
err = sbprof_zbprof_stop ( ) ;
2007-03-09 15:59:56 +00:00
mutex_unlock ( & sbp . lock ) ;
2005-04-16 15:20:36 -07:00
break ;
2007-03-09 15:59:56 +00:00
2007-03-29 11:39:56 -07:00
case SBPROF_ZBWAITFULL : {
err = wait_event_interruptible ( sbp . tb_read , TB_FULL ) ;
if ( err )
2007-03-09 15:59:56 +00:00
break ;
2007-03-29 11:39:56 -07:00
err = put_user ( TB_FULL , ( int * ) arg ) ;
2007-03-09 15:59:56 +00:00
break ;
2007-03-29 11:39:56 -07:00
}
2007-03-09 15:59:56 +00:00
2005-04-16 15:20:36 -07:00
default :
2007-03-29 11:39:56 -07:00
err = - EINVAL ;
2005-04-16 15:20:36 -07:00
break ;
}
2007-03-29 11:39:56 -07:00
return err ;
2005-04-16 15:20:36 -07:00
}
2007-02-12 00:55:31 -08:00
static const struct file_operations sbprof_tb_fops = {
2005-04-16 15:20:36 -07:00
. owner = THIS_MODULE ,
. open = sbprof_tb_open ,
. release = sbprof_tb_release ,
. read = sbprof_tb_read ,
2005-09-30 01:51:21 +01:00
. unlocked_ioctl = sbprof_tb_ioctl ,
. compat_ioctl = sbprof_tb_ioctl ,
2005-04-16 15:20:36 -07:00
. mmap = NULL ,
} ;
2007-03-09 15:59:56 +00:00
static struct class * tb_class ;
static struct device * tb_dev ;
2005-04-16 15:20:36 -07:00
static int __init sbprof_tb_init ( void )
{
2007-03-09 15:59:56 +00:00
struct device * dev ;
struct class * tbc ;
int err ;
2005-04-16 15:20:36 -07:00
if ( register_chrdev ( SBPROF_TB_MAJOR , DEVNAME , & sbprof_tb_fops ) ) {
printk ( KERN_WARNING DEVNAME " : initialization failed (dev %d) \n " ,
SBPROF_TB_MAJOR ) ;
return - EIO ;
}
2007-03-09 15:59:56 +00:00
tbc = class_create ( THIS_MODULE , " sb_tracebuffer " ) ;
if ( IS_ERR ( tbc ) ) {
err = PTR_ERR ( tbc ) ;
goto out_chrdev ;
}
tb_class = tbc ;
2008-05-21 12:52:33 -07:00
dev = device_create_drvdata ( tbc , NULL , MKDEV ( SBPROF_TB_MAJOR , 0 ) ,
NULL , " tb " ) ;
2007-03-09 15:59:56 +00:00
if ( IS_ERR ( dev ) ) {
err = PTR_ERR ( dev ) ;
goto out_class ;
}
tb_dev = dev ;
2005-04-16 15:20:36 -07:00
sbp . open = 0 ;
tb_period = zbbus_mhz * 10000LL ;
2007-03-29 11:39:56 -07:00
pr_info ( DEVNAME " : initialized - tb_period = %lld \n " ,
( long long ) tb_period ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
2007-03-09 15:59:56 +00:00
out_class :
class_destroy ( tb_class ) ;
out_chrdev :
unregister_chrdev ( SBPROF_TB_MAJOR , DEVNAME ) ;
return err ;
2005-04-16 15:20:36 -07:00
}
static void __exit sbprof_tb_cleanup ( void )
{
2007-03-09 15:59:56 +00:00
device_destroy ( tb_class , MKDEV ( SBPROF_TB_MAJOR , 0 ) ) ;
2005-04-16 15:20:36 -07:00
unregister_chrdev ( SBPROF_TB_MAJOR , DEVNAME ) ;
2007-03-09 15:59:56 +00:00
class_destroy ( tb_class ) ;
2005-04-16 15:20:36 -07:00
}
module_init ( sbprof_tb_init ) ;
module_exit ( sbprof_tb_cleanup ) ;
2007-03-09 15:59:56 +00:00
MODULE_ALIAS_CHARDEV_MAJOR ( SBPROF_TB_MAJOR ) ;
MODULE_AUTHOR ( " Ralf Baechle <ralf@linux-mips.org> " ) ;
MODULE_LICENSE ( " GPL " ) ;