2005-04-17 02:20:36 +04:00
/*
* Copyright ( c ) 2001 - 2002 by David Brownell
2006-08-31 01:50:06 +04:00
*
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 .
*
* 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 . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
/* this file is part of ehci-hcd.c */
# define ehci_dbg(ehci, fmt, args...) \
dev_dbg ( ehci_to_hcd ( ehci ) - > self . controller , fmt , # # args )
# define ehci_err(ehci, fmt, args...) \
dev_err ( ehci_to_hcd ( ehci ) - > self . controller , fmt , # # args )
# define ehci_info(ehci, fmt, args...) \
dev_info ( ehci_to_hcd ( ehci ) - > self . controller , fmt , # # args )
# define ehci_warn(ehci, fmt, args...) \
dev_warn ( ehci_to_hcd ( ehci ) - > self . controller , fmt , # # args )
2008-02-01 22:42:05 +03:00
# ifdef VERBOSE_DEBUG
2005-04-17 02:20:36 +04:00
# define vdbg dbg
# define ehci_vdbg ehci_dbg
# else
# define vdbg(fmt,args...) do { } while (0)
# define ehci_vdbg(ehci, fmt, args...) do { } while (0)
# endif
# ifdef DEBUG
/* check the values in the HCSPARAMS register
* ( host controller _Structural_ parameters )
* see EHCI spec , Table 2 - 4 for each value
*/
static void dbg_hcs_params ( struct ehci_hcd * ehci , char * label )
{
2006-12-14 22:54:08 +03:00
u32 params = ehci_readl ( ehci , & ehci - > caps - > hcs_params ) ;
2005-04-17 02:20:36 +04:00
ehci_dbg ( ehci ,
" %s hcs_params 0x%x dbg=%d%s cc=%d pcc=%d%s%s ports=%d \n " ,
label , params ,
HCS_DEBUG_PORT ( params ) ,
HCS_INDICATOR ( params ) ? " ind " : " " ,
HCS_N_CC ( params ) ,
HCS_N_PCC ( params ) ,
2007-05-01 20:29:37 +04:00
HCS_PORTROUTED ( params ) ? " " : " ordered " ,
2005-04-17 02:20:36 +04:00
HCS_PPC ( params ) ? " " : " !ppc " ,
HCS_N_PORTS ( params )
) ;
/* Port routing, per EHCI 0.95 Spec, Section 2.2.5 */
if ( HCS_PORTROUTED ( params ) ) {
int i ;
char buf [ 46 ] , tmp [ 7 ] , byte ;
buf [ 0 ] = 0 ;
for ( i = 0 ; i < HCS_N_PORTS ( params ) ; i + + ) {
// FIXME MIPS won't readb() ...
byte = readb ( & ehci - > caps - > portroute [ ( i > > 1 ) ] ) ;
2006-08-31 01:50:06 +04:00
sprintf ( tmp , " %d " ,
2005-04-17 02:20:36 +04:00
( ( i & 0x1 ) ? ( ( byte ) & 0xf ) : ( ( byte > > 4 ) & 0xf ) ) ) ;
strcat ( buf , tmp ) ;
}
ehci_dbg ( ehci , " %s portroute %s \n " ,
label , buf ) ;
}
}
# else
static inline void dbg_hcs_params ( struct ehci_hcd * ehci , char * label ) { }
# endif
# ifdef DEBUG
/* check the values in the HCCPARAMS register
* ( host controller _Capability_ parameters )
* see EHCI Spec , Table 2 - 5 for each value
* */
static void dbg_hcc_params ( struct ehci_hcd * ehci , char * label )
{
2006-12-14 22:54:08 +03:00
u32 params = ehci_readl ( ehci , & ehci - > caps - > hcc_params ) ;
2005-04-17 02:20:36 +04:00
if ( HCC_ISOC_CACHE ( params ) ) {
ehci_dbg ( ehci ,
2007-05-01 20:29:37 +04:00
" %s hcc_params %04x caching frame %s%s%s \n " ,
label , params ,
HCC_PGM_FRAMELISTLEN ( params ) ? " 256/512/1024 " : " 1024 " ,
HCC_CANPARK ( params ) ? " park " : " " ,
HCC_64BIT_ADDR ( params ) ? " 64 bit addr " : " " ) ;
2005-04-17 02:20:36 +04:00
} else {
ehci_dbg ( ehci ,
2007-05-01 20:29:37 +04:00
" %s hcc_params %04x thresh %d uframes %s%s%s \n " ,
label ,
params ,
HCC_ISOC_THRES ( params ) ,
HCC_PGM_FRAMELISTLEN ( params ) ? " 256/512/1024 " : " 1024 " ,
HCC_CANPARK ( params ) ? " park " : " " ,
HCC_64BIT_ADDR ( params ) ? " 64 bit addr " : " " ) ;
2005-04-17 02:20:36 +04:00
}
}
# else
static inline void dbg_hcc_params ( struct ehci_hcd * ehci , char * label ) { }
# endif
# ifdef DEBUG
2007-05-12 01:39:44 +04:00
static void __maybe_unused
2005-04-17 02:20:36 +04:00
dbg_qtd ( const char * label , struct ehci_hcd * ehci , struct ehci_qtd * qtd )
{
2007-05-01 20:29:37 +04:00
ehci_dbg ( ehci , " %s td %p n%08x %08x t%08x p0=%08x \n " , label , qtd ,
hc32_to_cpup ( ehci , & qtd - > hw_next ) ,
hc32_to_cpup ( ehci , & qtd - > hw_alt_next ) ,
hc32_to_cpup ( ehci , & qtd - > hw_token ) ,
hc32_to_cpup ( ehci , & qtd - > hw_buf [ 0 ] ) ) ;
2005-04-17 02:20:36 +04:00
if ( qtd - > hw_buf [ 1 ] )
2007-05-01 20:29:37 +04:00
ehci_dbg ( ehci , " p1=%08x p2=%08x p3=%08x p4=%08x \n " ,
hc32_to_cpup ( ehci , & qtd - > hw_buf [ 1 ] ) ,
hc32_to_cpup ( ehci , & qtd - > hw_buf [ 2 ] ) ,
hc32_to_cpup ( ehci , & qtd - > hw_buf [ 3 ] ) ,
hc32_to_cpup ( ehci , & qtd - > hw_buf [ 4 ] ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-05-12 01:39:44 +04:00
static void __maybe_unused
2005-04-17 02:20:36 +04:00
dbg_qh ( const char * label , struct ehci_hcd * ehci , struct ehci_qh * qh )
{
ehci_dbg ( ehci , " %s qh %p n%08x info %x %x qtd %x \n " , label ,
qh , qh - > hw_next , qh - > hw_info1 , qh - > hw_info2 ,
qh - > hw_current ) ;
dbg_qtd ( " overlay " , ehci , ( struct ehci_qtd * ) & qh - > hw_qtd_next ) ;
}
2007-05-12 01:39:44 +04:00
static void __maybe_unused
2006-08-31 01:50:06 +04:00
dbg_itd ( const char * label , struct ehci_hcd * ehci , struct ehci_itd * itd )
2005-04-17 02:20:36 +04:00
{
ehci_dbg ( ehci , " %s [%d] itd %p, next %08x, urb %p \n " ,
2007-05-01 20:29:37 +04:00
label , itd - > frame , itd , hc32_to_cpu ( ehci , itd - > hw_next ) ,
itd - > urb ) ;
2005-04-17 02:20:36 +04:00
ehci_dbg ( ehci ,
2006-08-31 01:50:06 +04:00
" trans: %08x %08x %08x %08x %08x %08x %08x %08x \n " ,
2007-05-01 20:29:37 +04:00
hc32_to_cpu ( ehci , itd - > hw_transaction [ 0 ] ) ,
hc32_to_cpu ( ehci , itd - > hw_transaction [ 1 ] ) ,
hc32_to_cpu ( ehci , itd - > hw_transaction [ 2 ] ) ,
hc32_to_cpu ( ehci , itd - > hw_transaction [ 3 ] ) ,
hc32_to_cpu ( ehci , itd - > hw_transaction [ 4 ] ) ,
hc32_to_cpu ( ehci , itd - > hw_transaction [ 5 ] ) ,
hc32_to_cpu ( ehci , itd - > hw_transaction [ 6 ] ) ,
hc32_to_cpu ( ehci , itd - > hw_transaction [ 7 ] ) ) ;
2005-04-17 02:20:36 +04:00
ehci_dbg ( ehci ,
2006-08-31 01:50:06 +04:00
" buf: %08x %08x %08x %08x %08x %08x %08x \n " ,
2007-05-01 20:29:37 +04:00
hc32_to_cpu ( ehci , itd - > hw_bufp [ 0 ] ) ,
hc32_to_cpu ( ehci , itd - > hw_bufp [ 1 ] ) ,
hc32_to_cpu ( ehci , itd - > hw_bufp [ 2 ] ) ,
hc32_to_cpu ( ehci , itd - > hw_bufp [ 3 ] ) ,
hc32_to_cpu ( ehci , itd - > hw_bufp [ 4 ] ) ,
hc32_to_cpu ( ehci , itd - > hw_bufp [ 5 ] ) ,
hc32_to_cpu ( ehci , itd - > hw_bufp [ 6 ] ) ) ;
2005-04-17 02:20:36 +04:00
ehci_dbg ( ehci , " index: %d %d %d %d %d %d %d %d \n " ,
itd - > index [ 0 ] , itd - > index [ 1 ] , itd - > index [ 2 ] ,
itd - > index [ 3 ] , itd - > index [ 4 ] , itd - > index [ 5 ] ,
itd - > index [ 6 ] , itd - > index [ 7 ] ) ;
}
2007-05-12 01:39:44 +04:00
static void __maybe_unused
2006-08-31 01:50:06 +04:00
dbg_sitd ( const char * label , struct ehci_hcd * ehci , struct ehci_sitd * sitd )
2005-04-17 02:20:36 +04:00
{
ehci_dbg ( ehci , " %s [%d] sitd %p, next %08x, urb %p \n " ,
2007-05-01 20:29:37 +04:00
label , sitd - > frame , sitd , hc32_to_cpu ( ehci , sitd - > hw_next ) ,
sitd - > urb ) ;
2005-04-17 02:20:36 +04:00
ehci_dbg ( ehci ,
2006-08-31 01:50:06 +04:00
" addr %08x sched %04x result %08x buf %08x %08x \n " ,
2007-05-01 20:29:37 +04:00
hc32_to_cpu ( ehci , sitd - > hw_fullspeed_ep ) ,
hc32_to_cpu ( ehci , sitd - > hw_uframe ) ,
hc32_to_cpu ( ehci , sitd - > hw_results ) ,
hc32_to_cpu ( ehci , sitd - > hw_buf [ 0 ] ) ,
hc32_to_cpu ( ehci , sitd - > hw_buf [ 1 ] ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-05-12 01:39:44 +04:00
static int __maybe_unused
2005-04-17 02:20:36 +04:00
dbg_status_buf ( char * buf , unsigned len , const char * label , u32 status )
{
return scnprintf ( buf , len ,
" %s%sstatus %04x%s%s%s%s%s%s%s%s%s%s " ,
label , label [ 0 ] ? " " : " " , status ,
( status & STS_ASS ) ? " Async " : " " ,
( status & STS_PSS ) ? " Periodic " : " " ,
( status & STS_RECL ) ? " Recl " : " " ,
( status & STS_HALT ) ? " Halt " : " " ,
( status & STS_IAA ) ? " IAA " : " " ,
( status & STS_FATAL ) ? " FATAL " : " " ,
( status & STS_FLR ) ? " FLR " : " " ,
( status & STS_PCD ) ? " PCD " : " " ,
( status & STS_ERR ) ? " ERR " : " " ,
( status & STS_INT ) ? " INT " : " "
) ;
}
2007-05-12 01:39:44 +04:00
static int __maybe_unused
2005-04-17 02:20:36 +04:00
dbg_intr_buf ( char * buf , unsigned len , const char * label , u32 enable )
{
return scnprintf ( buf , len ,
" %s%sintrenable %02x%s%s%s%s%s%s " ,
label , label [ 0 ] ? " " : " " , enable ,
( enable & STS_IAA ) ? " IAA " : " " ,
( enable & STS_FATAL ) ? " FATAL " : " " ,
( enable & STS_FLR ) ? " FLR " : " " ,
( enable & STS_PCD ) ? " PCD " : " " ,
( enable & STS_ERR ) ? " ERR " : " " ,
( enable & STS_INT ) ? " INT " : " "
) ;
}
static const char * const fls_strings [ ] =
{ " 1024 " , " 512 " , " 256 " , " ?? " } ;
static int
dbg_command_buf ( char * buf , unsigned len , const char * label , u32 command )
{
return scnprintf ( buf , len ,
" %s%scommand %06x %s=%d ithresh=%d%s%s%s%s period=%s%s %s " ,
label , label [ 0 ] ? " " : " " , command ,
( command & CMD_PARK ) ? " park " : " (park) " ,
CMD_PARK_CNT ( command ) ,
( command > > 16 ) & 0x3f ,
( command & CMD_LRESET ) ? " LReset " : " " ,
( command & CMD_IAAD ) ? " IAAD " : " " ,
( command & CMD_ASE ) ? " Async " : " " ,
( command & CMD_PSE ) ? " Periodic " : " " ,
fls_strings [ ( command > > 2 ) & 0x3 ] ,
( command & CMD_RESET ) ? " Reset " : " " ,
( command & CMD_RUN ) ? " RUN " : " HALT "
) ;
}
static int
dbg_port_buf ( char * buf , unsigned len , const char * label , int port , u32 status )
{
char * sig ;
/* signaling state */
switch ( status & ( 3 < < 10 ) ) {
case 0 < < 10 : sig = " se0 " ; break ;
case 1 < < 10 : sig = " k " ; break ; /* low speed */
case 2 < < 10 : sig = " j " ; break ;
default : sig = " ? " ; break ;
}
return scnprintf ( buf , len ,
2005-05-08 00:21:50 +04:00
" %s%sport %d status %06x%s%s sig=%s%s%s%s%s%s%s%s%s%s " ,
2005-04-17 02:20:36 +04:00
label , label [ 0 ] ? " " : " " , port , status ,
( status & PORT_POWER ) ? " POWER " : " " ,
( status & PORT_OWNER ) ? " OWNER " : " " ,
sig ,
( status & PORT_RESET ) ? " RESET " : " " ,
( status & PORT_SUSPEND ) ? " SUSPEND " : " " ,
( status & PORT_RESUME ) ? " RESUME " : " " ,
( status & PORT_OCC ) ? " OCC " : " " ,
( status & PORT_OC ) ? " OC " : " " ,
( status & PORT_PEC ) ? " PEC " : " " ,
( status & PORT_PE ) ? " PE " : " " ,
( status & PORT_CSC ) ? " CSC " : " " ,
2007-05-01 20:29:37 +04:00
( status & PORT_CONNECT ) ? " CONNECT " : " " ) ;
2005-04-17 02:20:36 +04:00
}
# else
2007-05-12 01:39:44 +04:00
static inline void __maybe_unused
2005-04-17 02:20:36 +04:00
dbg_qh ( char * label , struct ehci_hcd * ehci , struct ehci_qh * qh )
{ }
2007-05-12 01:39:44 +04:00
static inline int __maybe_unused
2005-04-17 02:20:36 +04:00
dbg_status_buf ( char * buf , unsigned len , const char * label , u32 status )
{ return 0 ; }
2007-05-12 01:39:44 +04:00
static inline int __maybe_unused
2005-04-17 02:20:36 +04:00
dbg_command_buf ( char * buf , unsigned len , const char * label , u32 command )
{ return 0 ; }
2007-05-12 01:39:44 +04:00
static inline int __maybe_unused
2005-04-17 02:20:36 +04:00
dbg_intr_buf ( char * buf , unsigned len , const char * label , u32 enable )
{ return 0 ; }
2007-05-12 01:39:44 +04:00
static inline int __maybe_unused
2005-04-17 02:20:36 +04:00
dbg_port_buf ( char * buf , unsigned len , const char * label , int port , u32 status )
{ return 0 ; }
# endif /* DEBUG */
/* functions have the "wrong" filename when they're output... */
# define dbg_status(ehci, label, status) { \
char _buf [ 80 ] ; \
dbg_status_buf ( _buf , sizeof _buf , label , status ) ; \
ehci_dbg ( ehci , " %s \n " , _buf ) ; \
}
# define dbg_cmd(ehci, label, command) { \
char _buf [ 80 ] ; \
dbg_command_buf ( _buf , sizeof _buf , label , command ) ; \
ehci_dbg ( ehci , " %s \n " , _buf ) ; \
}
# define dbg_port(ehci, label, port, status) { \
char _buf [ 80 ] ; \
dbg_port_buf ( _buf , sizeof _buf , label , port , status ) ; \
ehci_dbg ( ehci , " %s \n " , _buf ) ; \
}
/*-------------------------------------------------------------------------*/
# ifdef STUB_DEBUG_FILES
static inline void create_debug_files ( struct ehci_hcd * bus ) { }
static inline void remove_debug_files ( struct ehci_hcd * bus ) { }
# else
2007-09-12 01:07:31 +04:00
/* troubleshooting help: expose state in debugfs */
static int debug_async_open ( struct inode * , struct file * ) ;
static int debug_periodic_open ( struct inode * , struct file * ) ;
static int debug_registers_open ( struct inode * , struct file * ) ;
static int debug_async_open ( struct inode * , struct file * ) ;
static ssize_t debug_output ( struct file * , char __user * , size_t , loff_t * ) ;
static int debug_close ( struct inode * , struct file * ) ;
static const struct file_operations debug_async_fops = {
. owner = THIS_MODULE ,
. open = debug_async_open ,
. read = debug_output ,
. release = debug_close ,
} ;
static const struct file_operations debug_periodic_fops = {
. owner = THIS_MODULE ,
. open = debug_periodic_open ,
. read = debug_output ,
. release = debug_close ,
} ;
static const struct file_operations debug_registers_fops = {
. owner = THIS_MODULE ,
. open = debug_registers_open ,
. read = debug_output ,
. release = debug_close ,
} ;
static struct dentry * ehci_debug_root ;
struct debug_buffer {
ssize_t ( * fill_func ) ( struct debug_buffer * ) ; /* fill method */
struct usb_bus * bus ;
struct mutex mutex ; /* protect filling of buffer */
size_t count ; /* number of characters filled into buffer */
2008-09-18 19:06:21 +04:00
char * output_buf ;
size_t alloc_size ;
2007-09-12 01:07:31 +04:00
} ;
2005-04-17 02:20:36 +04:00
# define speed_char(info1) ({ char tmp; \
switch ( info1 & ( 3 < < 12 ) ) { \
case 0 < < 12 : tmp = ' f ' ; break ; \
case 1 < < 12 : tmp = ' l ' ; break ; \
case 2 < < 12 : tmp = ' h ' ; break ; \
default : tmp = ' ? ' ; break ; \
} ; tmp ; } )
2007-05-01 20:29:37 +04:00
static inline char token_mark ( struct ehci_hcd * ehci , __hc32 token )
2005-04-17 02:20:36 +04:00
{
2007-05-01 20:29:37 +04:00
__u32 v = hc32_to_cpu ( ehci , token ) ;
2005-04-17 02:20:36 +04:00
if ( v & QTD_STS_ACTIVE )
return ' * ' ;
if ( v & QTD_STS_HALT )
return ' - ' ;
if ( ! IS_SHORT_READ ( v ) )
return ' ' ;
/* tries to advance through hw_alt_next */
return ' / ' ;
}
static void qh_lines (
struct ehci_hcd * ehci ,
struct ehci_qh * qh ,
char * * nextp ,
unsigned * sizep
)
{
u32 scratch ;
u32 hw_curr ;
struct list_head * entry ;
struct ehci_qtd * td ;
unsigned temp ;
unsigned size = * sizep ;
char * next = * nextp ;
char mark ;
2008-04-28 10:00:16 +04:00
__le32 list_end = EHCI_LIST_END ( ehci ) ;
2005-04-17 02:20:36 +04:00
2007-05-01 20:29:37 +04:00
if ( qh - > hw_qtd_next = = list_end ) /* NEC does this */
2005-04-17 02:20:36 +04:00
mark = ' @ ' ;
else
2007-05-01 20:29:37 +04:00
mark = token_mark ( ehci , qh - > hw_token ) ;
2005-04-17 02:20:36 +04:00
if ( mark = = ' / ' ) { /* qh_alt_next controls qh advance? */
2007-05-01 20:29:37 +04:00
if ( ( qh - > hw_alt_next & QTD_MASK ( ehci ) )
= = ehci - > async - > hw_alt_next )
2005-04-17 02:20:36 +04:00
mark = ' # ' ; /* blocked */
2007-05-01 20:29:37 +04:00
else if ( qh - > hw_alt_next = = list_end )
2005-04-17 02:20:36 +04:00
mark = ' . ' ; /* use hw_qtd_next */
/* else alt_next points to some other qtd */
}
2007-05-01 20:29:37 +04:00
scratch = hc32_to_cpup ( ehci , & qh - > hw_info1 ) ;
hw_curr = ( mark = = ' * ' ) ? hc32_to_cpup ( ehci , & qh - > hw_current ) : 0 ;
2005-04-17 02:20:36 +04:00
temp = scnprintf ( next , size ,
" qh/%p dev%d %cs ep%d %08x %08x (%08x%c %s nak%d) " ,
qh , scratch & 0x007f ,
speed_char ( scratch ) ,
( scratch > > 8 ) & 0x000f ,
2007-05-01 20:29:37 +04:00
scratch , hc32_to_cpup ( ehci , & qh - > hw_info2 ) ,
hc32_to_cpup ( ehci , & qh - > hw_token ) , mark ,
( cpu_to_hc32 ( ehci , QTD_TOGGLE ) & qh - > hw_token )
2005-04-17 02:20:36 +04:00
? " data1 " : " data0 " ,
2007-05-01 20:29:37 +04:00
( hc32_to_cpup ( ehci , & qh - > hw_alt_next ) > > 1 ) & 0x0f ) ;
2005-04-17 02:20:36 +04:00
size - = temp ;
next + = temp ;
/* hc may be modifying the list as we read it ... */
list_for_each ( entry , & qh - > qtd_list ) {
td = list_entry ( entry , struct ehci_qtd , qtd_list ) ;
2007-05-01 20:29:37 +04:00
scratch = hc32_to_cpup ( ehci , & td - > hw_token ) ;
2005-04-17 02:20:36 +04:00
mark = ' ' ;
if ( hw_curr = = td - > qtd_dma )
mark = ' * ' ;
2007-05-01 20:29:37 +04:00
else if ( qh - > hw_qtd_next = = cpu_to_hc32 ( ehci , td - > qtd_dma ) )
2005-04-17 02:20:36 +04:00
mark = ' + ' ;
else if ( QTD_LENGTH ( scratch ) ) {
if ( td - > hw_alt_next = = ehci - > async - > hw_alt_next )
mark = ' # ' ;
2007-05-01 20:29:37 +04:00
else if ( td - > hw_alt_next ! = list_end )
2005-04-17 02:20:36 +04:00
mark = ' / ' ;
}
temp = snprintf ( next , size ,
" \n \t %p%c%s len=%d %08x urb %p " ,
td , mark , ( { char * tmp ;
switch ( ( scratch > > 8 ) & 0x03 ) {
case 0 : tmp = " out " ; break ;
case 1 : tmp = " in " ; break ;
case 2 : tmp = " setup " ; break ;
default : tmp = " ? " ; break ;
} tmp ; } ) ,
( scratch > > 16 ) & 0x7fff ,
scratch ,
td - > urb ) ;
2008-10-21 08:47:23 +04:00
if ( size < temp )
2005-04-17 02:20:36 +04:00
temp = size ;
size - = temp ;
next + = temp ;
if ( temp = = size )
goto done ;
}
temp = snprintf ( next , size , " \n " ) ;
2008-10-21 08:47:23 +04:00
if ( size < temp )
2005-04-17 02:20:36 +04:00
temp = size ;
size - = temp ;
next + = temp ;
done :
* sizep = size ;
* nextp = next ;
}
2007-09-12 01:07:31 +04:00
static ssize_t fill_async_buffer ( struct debug_buffer * buf )
2005-04-17 02:20:36 +04:00
{
struct usb_hcd * hcd ;
struct ehci_hcd * ehci ;
unsigned long flags ;
unsigned temp , size ;
char * next ;
struct ehci_qh * qh ;
2007-09-12 01:07:31 +04:00
hcd = bus_to_hcd ( buf - > bus ) ;
2005-04-17 02:20:36 +04:00
ehci = hcd_to_ehci ( hcd ) ;
2008-09-18 19:06:21 +04:00
next = buf - > output_buf ;
size = buf - > alloc_size ;
2005-04-17 02:20:36 +04:00
2007-09-12 01:07:31 +04:00
* next = 0 ;
2005-04-17 02:20:36 +04:00
/* dumps a snapshot of the async schedule.
* usually empty except for long - term bulk reads , or head .
* one QH per line , and TDs we know about
*/
spin_lock_irqsave ( & ehci - > lock , flags ) ;
for ( qh = ehci - > async - > qh_next . qh ; size > 0 & & qh ; qh = qh - > qh_next . qh )
qh_lines ( ehci , qh , & next , & size ) ;
if ( ehci - > reclaim & & size > 0 ) {
temp = scnprintf ( next , size , " \n reclaim = \n " ) ;
size - = temp ;
next + = temp ;
for ( qh = ehci - > reclaim ; size > 0 & & qh ; qh = qh - > reclaim )
qh_lines ( ehci , qh , & next , & size ) ;
}
spin_unlock_irqrestore ( & ehci - > lock , flags ) ;
2008-09-18 19:06:21 +04:00
return strlen ( buf - > output_buf ) ;
2005-04-17 02:20:36 +04:00
}
# define DBG_SCHED_LIMIT 64
2007-09-12 01:07:31 +04:00
static ssize_t fill_periodic_buffer ( struct debug_buffer * buf )
2005-04-17 02:20:36 +04:00
{
struct usb_hcd * hcd ;
struct ehci_hcd * ehci ;
unsigned long flags ;
union ehci_shadow p , * seen ;
unsigned temp , size , seen_count ;
char * next ;
unsigned i ;
2007-05-01 20:29:37 +04:00
__hc32 tag ;
2005-04-17 02:20:36 +04:00
2006-12-07 07:33:16 +03:00
if ( ! ( seen = kmalloc ( DBG_SCHED_LIMIT * sizeof * seen , GFP_ATOMIC ) ) )
2005-04-17 02:20:36 +04:00
return 0 ;
seen_count = 0 ;
2007-09-12 01:07:31 +04:00
hcd = bus_to_hcd ( buf - > bus ) ;
2005-04-17 02:20:36 +04:00
ehci = hcd_to_ehci ( hcd ) ;
2008-09-18 19:06:21 +04:00
next = buf - > output_buf ;
size = buf - > alloc_size ;
2005-04-17 02:20:36 +04:00
temp = scnprintf ( next , size , " size = %d \n " , ehci - > periodic_size ) ;
size - = temp ;
next + = temp ;
/* dump a snapshot of the periodic schedule.
* iso changes , interrupt usually doesn ' t .
*/
spin_lock_irqsave ( & ehci - > lock , flags ) ;
for ( i = 0 ; i < ehci - > periodic_size ; i + + ) {
p = ehci - > pshadow [ i ] ;
if ( likely ( ! p . ptr ) )
continue ;
2007-05-01 20:29:37 +04:00
tag = Q_NEXT_TYPE ( ehci , ehci - > periodic [ i ] ) ;
2005-04-17 02:20:36 +04:00
temp = scnprintf ( next , size , " %4d: " , i ) ;
size - = temp ;
next + = temp ;
do {
2007-05-01 20:29:37 +04:00
switch ( hc32_to_cpu ( ehci , tag ) ) {
2005-04-17 02:20:36 +04:00
case Q_TYPE_QH :
temp = scnprintf ( next , size , " qh%d-%04x/%p " ,
p . qh - > period ,
2007-05-01 20:29:37 +04:00
hc32_to_cpup ( ehci ,
& p . qh - > hw_info2 )
2005-04-17 02:20:36 +04:00
/* uframe masks */
2005-08-05 05:06:41 +04:00
& ( QH_CMASK | QH_SMASK ) ,
2005-04-17 02:20:36 +04:00
p . qh ) ;
size - = temp ;
next + = temp ;
/* don't repeat what follows this qh */
for ( temp = 0 ; temp < seen_count ; temp + + ) {
if ( seen [ temp ] . ptr ! = p . ptr )
continue ;
2008-09-18 19:06:38 +04:00
if ( p . qh - > qh_next . ptr ) {
2005-04-17 02:20:36 +04:00
temp = scnprintf ( next , size ,
" ... " ) ;
2008-09-18 19:06:38 +04:00
size - = temp ;
next + = temp ;
}
2005-04-17 02:20:36 +04:00
break ;
}
/* show more info the first time around */
2008-09-18 19:06:38 +04:00
if ( temp = = seen_count ) {
2007-05-01 20:29:37 +04:00
u32 scratch = hc32_to_cpup ( ehci ,
2005-04-17 02:20:36 +04:00
& p . qh - > hw_info1 ) ;
struct ehci_qtd * qtd ;
char * type = " " ;
/* count tds, get ep direction */
temp = 0 ;
list_for_each_entry ( qtd ,
& p . qh - > qtd_list ,
qtd_list ) {
temp + + ;
2007-05-01 20:29:37 +04:00
switch ( 0x03 & ( hc32_to_cpu (
ehci ,
2005-04-17 02:20:36 +04:00
qtd - > hw_token ) > > 8 ) ) {
case 0 : type = " out " ; continue ;
case 1 : type = " in " ; continue ;
}
}
temp = scnprintf ( next , size ,
" (%c%d ep%d%s "
" [%d/%d] q%d p%d) " ,
speed_char ( scratch ) ,
scratch & 0x007f ,
( scratch > > 8 ) & 0x000f , type ,
p . qh - > usecs , p . qh - > c_usecs ,
temp ,
0x7ff & ( scratch > > 16 ) ) ;
if ( seen_count < DBG_SCHED_LIMIT )
seen [ seen_count + + ] . qh = p . qh ;
} else
temp = 0 ;
if ( p . qh ) {
2007-05-01 20:29:37 +04:00
tag = Q_NEXT_TYPE ( ehci , p . qh - > hw_next ) ;
2005-04-17 02:20:36 +04:00
p = p . qh - > qh_next ;
}
break ;
case Q_TYPE_FSTN :
temp = scnprintf ( next , size ,
" fstn-%8x/%p " , p . fstn - > hw_prev ,
p . fstn ) ;
2007-05-01 20:29:37 +04:00
tag = Q_NEXT_TYPE ( ehci , p . fstn - > hw_next ) ;
2005-04-17 02:20:36 +04:00
p = p . fstn - > fstn_next ;
break ;
case Q_TYPE_ITD :
temp = scnprintf ( next , size ,
" itd/%p " , p . itd ) ;
2007-05-01 20:29:37 +04:00
tag = Q_NEXT_TYPE ( ehci , p . itd - > hw_next ) ;
2005-04-17 02:20:36 +04:00
p = p . itd - > itd_next ;
break ;
case Q_TYPE_SITD :
temp = scnprintf ( next , size ,
" sitd%d-%04x/%p " ,
p . sitd - > stream - > interval ,
2007-05-01 20:29:37 +04:00
hc32_to_cpup ( ehci , & p . sitd - > hw_uframe )
2005-04-17 02:20:36 +04:00
& 0x0000ffff ,
p . sitd ) ;
2007-05-01 20:29:37 +04:00
tag = Q_NEXT_TYPE ( ehci , p . sitd - > hw_next ) ;
2005-04-17 02:20:36 +04:00
p = p . sitd - > sitd_next ;
break ;
}
size - = temp ;
next + = temp ;
} while ( p . ptr ) ;
temp = scnprintf ( next , size , " \n " ) ;
size - = temp ;
next + = temp ;
}
spin_unlock_irqrestore ( & ehci - > lock , flags ) ;
kfree ( seen ) ;
2008-09-18 19:06:21 +04:00
return buf - > alloc_size - size ;
2005-04-17 02:20:36 +04:00
}
# undef DBG_SCHED_LIMIT
2007-09-12 01:07:31 +04:00
static ssize_t fill_registers_buffer ( struct debug_buffer * buf )
2005-04-17 02:20:36 +04:00
{
struct usb_hcd * hcd ;
struct ehci_hcd * ehci ;
unsigned long flags ;
unsigned temp , size , i ;
char * next , scratch [ 80 ] ;
static char fmt [ ] = " %*s \n " ;
static char label [ ] = " " ;
2007-09-12 01:07:31 +04:00
hcd = bus_to_hcd ( buf - > bus ) ;
2005-04-17 02:20:36 +04:00
ehci = hcd_to_ehci ( hcd ) ;
2008-09-18 19:06:21 +04:00
next = buf - > output_buf ;
size = buf - > alloc_size ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & ehci - > lock , flags ) ;
2008-03-07 01:00:58 +03:00
if ( ! test_bit ( HCD_FLAG_HW_ACCESSIBLE , & hcd - > flags ) ) {
2005-04-17 02:20:36 +04:00
size = scnprintf ( next , size ,
2008-10-02 19:47:15 +04:00
" bus %s, device %s \n "
2005-05-08 00:21:50 +04:00
" %s \n "
2005-04-17 02:20:36 +04:00
" SUSPENDED (no register access) \n " ,
hcd - > self . controller - > bus - > name ,
2008-05-02 08:02:41 +04:00
dev_name ( hcd - > self . controller ) ,
2005-05-08 00:21:50 +04:00
hcd - > product_desc ) ;
2005-04-17 02:20:36 +04:00
goto done ;
}
/* Capability Registers */
2006-12-14 22:54:08 +03:00
i = HC_VERSION ( ehci_readl ( ehci , & ehci - > caps - > hc_capbase ) ) ;
2005-04-17 02:20:36 +04:00
temp = scnprintf ( next , size ,
2008-10-02 19:47:15 +04:00
" bus %s, device %s \n "
2005-05-08 00:21:50 +04:00
" %s \n "
2005-04-17 02:20:36 +04:00
" EHCI %x.%02x, hcd state %d \n " ,
hcd - > self . controller - > bus - > name ,
2008-05-02 08:02:41 +04:00
dev_name ( hcd - > self . controller ) ,
2005-05-08 00:21:50 +04:00
hcd - > product_desc ,
2005-04-17 02:20:36 +04:00
i > > 8 , i & 0x0ff , hcd - > state ) ;
size - = temp ;
next + = temp ;
2005-05-08 00:21:50 +04:00
# ifdef CONFIG_PCI
/* EHCI 0.96 and later may have "extended capabilities" */
if ( hcd - > self . controller - > bus = = & pci_bus_type ) {
struct pci_dev * pdev ;
u32 offset , cap , cap2 ;
unsigned count = 256 / 4 ;
pdev = to_pci_dev ( ehci_to_hcd ( ehci ) - > self . controller ) ;
2007-05-01 20:29:37 +04:00
offset = HCC_EXT_CAPS ( ehci_readl ( ehci ,
& ehci - > caps - > hcc_params ) ) ;
2005-05-08 00:21:50 +04:00
while ( offset & & count - - ) {
pci_read_config_dword ( pdev , offset , & cap ) ;
switch ( cap & 0xff ) {
case 1 :
temp = scnprintf ( next , size ,
" ownership %08x%s%s \n " , cap ,
( cap & ( 1 < < 24 ) ) ? " linux " : " " ,
( cap & ( 1 < < 16 ) ) ? " firmware " : " " ) ;
size - = temp ;
next + = temp ;
offset + = 4 ;
pci_read_config_dword ( pdev , offset , & cap2 ) ;
temp = scnprintf ( next , size ,
" SMI sts/enable 0x%08x \n " , cap2 ) ;
size - = temp ;
next + = temp ;
break ;
case 0 : /* illegal reserved capability */
cap = 0 ;
/* FALLTHROUGH */
default : /* unknown */
break ;
}
temp = ( cap > > 8 ) & 0xff ;
}
}
# endif
2005-04-17 02:20:36 +04:00
// FIXME interpret both types of params
2006-12-14 22:54:08 +03:00
i = ehci_readl ( ehci , & ehci - > caps - > hcs_params ) ;
2005-04-17 02:20:36 +04:00
temp = scnprintf ( next , size , " structural params 0x%08x \n " , i ) ;
size - = temp ;
next + = temp ;
2006-12-14 22:54:08 +03:00
i = ehci_readl ( ehci , & ehci - > caps - > hcc_params ) ;
2005-04-17 02:20:36 +04:00
temp = scnprintf ( next , size , " capability params 0x%08x \n " , i ) ;
size - = temp ;
next + = temp ;
/* Operational Registers */
temp = dbg_status_buf ( scratch , sizeof scratch , label ,
2006-12-14 22:54:08 +03:00
ehci_readl ( ehci , & ehci - > regs - > status ) ) ;
2005-04-17 02:20:36 +04:00
temp = scnprintf ( next , size , fmt , temp , scratch ) ;
size - = temp ;
next + = temp ;
temp = dbg_command_buf ( scratch , sizeof scratch , label ,
2006-12-14 22:54:08 +03:00
ehci_readl ( ehci , & ehci - > regs - > command ) ) ;
2005-04-17 02:20:36 +04:00
temp = scnprintf ( next , size , fmt , temp , scratch ) ;
size - = temp ;
next + = temp ;
temp = dbg_intr_buf ( scratch , sizeof scratch , label ,
2006-12-14 22:54:08 +03:00
ehci_readl ( ehci , & ehci - > regs - > intr_enable ) ) ;
2005-04-17 02:20:36 +04:00
temp = scnprintf ( next , size , fmt , temp , scratch ) ;
size - = temp ;
next + = temp ;
temp = scnprintf ( next , size , " uframe %04x \n " ,
2006-12-14 22:54:08 +03:00
ehci_readl ( ehci , & ehci - > regs - > frame_index ) ) ;
2005-04-17 02:20:36 +04:00
size - = temp ;
next + = temp ;
2005-05-08 00:21:50 +04:00
for ( i = 1 ; i < = HCS_N_PORTS ( ehci - > hcs_params ) ; i + + ) {
temp = dbg_port_buf ( scratch , sizeof scratch , label , i ,
2007-05-01 20:29:37 +04:00
ehci_readl ( ehci ,
& ehci - > regs - > port_status [ i - 1 ] ) ) ;
2005-04-17 02:20:36 +04:00
temp = scnprintf ( next , size , fmt , temp , scratch ) ;
size - = temp ;
next + = temp ;
2005-05-08 00:21:50 +04:00
if ( i = = HCS_DEBUG_PORT ( ehci - > hcs_params ) & & ehci - > debug ) {
temp = scnprintf ( next , size ,
" debug control %08x \n " ,
2007-05-01 20:29:37 +04:00
ehci_readl ( ehci ,
& ehci - > debug - > control ) ) ;
2005-05-08 00:21:50 +04:00
size - = temp ;
next + = temp ;
}
2005-04-17 02:20:36 +04:00
}
if ( ehci - > reclaim ) {
2007-12-12 00:05:30 +03:00
temp = scnprintf ( next , size , " reclaim qh %p \n " , ehci - > reclaim ) ;
2005-04-17 02:20:36 +04:00
size - = temp ;
next + = temp ;
}
# ifdef EHCI_STATS
temp = scnprintf ( next , size ,
" irq normal %ld err %ld reclaim %ld (lost %ld) \n " ,
ehci - > stats . normal , ehci - > stats . error , ehci - > stats . reclaim ,
ehci - > stats . lost_iaa ) ;
size - = temp ;
next + = temp ;
temp = scnprintf ( next , size , " complete %ld unlink %ld \n " ,
ehci - > stats . complete , ehci - > stats . unlink ) ;
size - = temp ;
next + = temp ;
# endif
done :
spin_unlock_irqrestore ( & ehci - > lock , flags ) ;
2008-09-18 19:06:21 +04:00
return buf - > alloc_size - size ;
2005-04-17 02:20:36 +04:00
}
2007-09-12 01:07:31 +04:00
static struct debug_buffer * alloc_buffer ( struct usb_bus * bus ,
ssize_t ( * fill_func ) ( struct debug_buffer * ) )
2005-04-17 02:20:36 +04:00
{
2007-09-12 01:07:31 +04:00
struct debug_buffer * buf ;
buf = kzalloc ( sizeof ( struct debug_buffer ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
2007-09-12 01:07:31 +04:00
if ( buf ) {
buf - > bus = bus ;
buf - > fill_func = fill_func ;
mutex_init ( & buf - > mutex ) ;
2008-09-18 19:06:21 +04:00
buf - > alloc_size = PAGE_SIZE ;
2007-09-12 01:07:31 +04:00
}
return buf ;
2005-04-17 02:20:36 +04:00
}
2007-09-12 01:07:31 +04:00
static int fill_buffer ( struct debug_buffer * buf )
2005-04-17 02:20:36 +04:00
{
2007-09-12 01:07:31 +04:00
int ret = 0 ;
2008-09-18 19:06:21 +04:00
if ( ! buf - > output_buf )
buf - > output_buf = ( char * ) vmalloc ( buf - > alloc_size ) ;
2007-09-12 01:07:31 +04:00
2008-09-18 19:06:21 +04:00
if ( ! buf - > output_buf ) {
2007-09-12 01:07:31 +04:00
ret = - ENOMEM ;
goto out ;
}
ret = buf - > fill_func ( buf ) ;
2005-04-17 02:20:36 +04:00
2007-09-12 01:07:31 +04:00
if ( ret > = 0 ) {
buf - > count = ret ;
ret = 0 ;
}
out :
return ret ;
2005-04-17 02:20:36 +04:00
}
2007-09-12 01:07:31 +04:00
static ssize_t debug_output ( struct file * file , char __user * user_buf ,
size_t len , loff_t * offset )
{
struct debug_buffer * buf = file - > private_data ;
int ret = 0 ;
mutex_lock ( & buf - > mutex ) ;
if ( buf - > count = = 0 ) {
ret = fill_buffer ( buf ) ;
if ( ret ! = 0 ) {
mutex_unlock ( & buf - > mutex ) ;
goto out ;
}
}
mutex_unlock ( & buf - > mutex ) ;
ret = simple_read_from_buffer ( user_buf , len , offset ,
2008-09-18 19:06:21 +04:00
buf - > output_buf , buf - > count ) ;
2007-09-12 01:07:31 +04:00
out :
return ret ;
}
static int debug_close ( struct inode * inode , struct file * file )
{
struct debug_buffer * buf = file - > private_data ;
2005-04-17 02:20:36 +04:00
2007-09-12 01:07:31 +04:00
if ( buf ) {
2008-09-18 19:06:21 +04:00
if ( buf - > output_buf )
vfree ( buf - > output_buf ) ;
2007-09-12 01:07:31 +04:00
kfree ( buf ) ;
}
return 0 ;
}
static int debug_async_open ( struct inode * inode , struct file * file )
{
file - > private_data = alloc_buffer ( inode - > i_private , fill_async_buffer ) ;
return file - > private_data ? 0 : - ENOMEM ;
}
static int debug_periodic_open ( struct inode * inode , struct file * file )
{
2008-09-18 19:06:21 +04:00
struct debug_buffer * buf ;
buf = alloc_buffer ( inode - > i_private , fill_periodic_buffer ) ;
if ( ! buf )
return - ENOMEM ;
2007-09-12 01:07:31 +04:00
2008-09-18 19:06:21 +04:00
buf - > alloc_size = ( sizeof ( void * ) = = 4 ? 6 : 8 ) * PAGE_SIZE ;
file - > private_data = buf ;
return 0 ;
2007-09-12 01:07:31 +04:00
}
static int debug_registers_open ( struct inode * inode , struct file * file )
{
file - > private_data = alloc_buffer ( inode - > i_private ,
fill_registers_buffer ) ;
return file - > private_data ? 0 : - ENOMEM ;
}
static inline void create_debug_files ( struct ehci_hcd * ehci )
{
struct usb_bus * bus = & ehci_to_hcd ( ehci ) - > self ;
ehci - > debug_dir = debugfs_create_dir ( bus - > bus_name , ehci_debug_root ) ;
if ( ! ehci - > debug_dir )
goto dir_error ;
ehci - > debug_async = debugfs_create_file ( " async " , S_IRUGO ,
ehci - > debug_dir , bus ,
& debug_async_fops ) ;
if ( ! ehci - > debug_async )
goto async_error ;
ehci - > debug_periodic = debugfs_create_file ( " periodic " , S_IRUGO ,
ehci - > debug_dir , bus ,
& debug_periodic_fops ) ;
if ( ! ehci - > debug_periodic )
goto periodic_error ;
ehci - > debug_registers = debugfs_create_file ( " registers " , S_IRUGO ,
ehci - > debug_dir , bus ,
& debug_registers_fops ) ;
if ( ! ehci - > debug_registers )
goto registers_error ;
return ;
registers_error :
debugfs_remove ( ehci - > debug_periodic ) ;
periodic_error :
debugfs_remove ( ehci - > debug_async ) ;
async_error :
debugfs_remove ( ehci - > debug_dir ) ;
dir_error :
ehci - > debug_periodic = NULL ;
ehci - > debug_async = NULL ;
ehci - > debug_dir = NULL ;
}
static inline void remove_debug_files ( struct ehci_hcd * ehci )
{
debugfs_remove ( ehci - > debug_registers ) ;
debugfs_remove ( ehci - > debug_periodic ) ;
debugfs_remove ( ehci - > debug_async ) ;
debugfs_remove ( ehci - > debug_dir ) ;
}
# endif /* STUB_DEBUG_FILES */