2012-05-11 18:25:46 +04:00
# include <linux/kernel.h>
2013-03-30 04:46:17 +04:00
# include <linux/device.h>
# include <linux/types.h>
# include <linux/spinlock.h>
2013-03-30 14:53:51 +04:00
# include <linux/debugfs.h>
# include <linux/seq_file.h>
# include <linux/uaccess.h>
2012-05-11 18:25:46 +04:00
# include <linux/usb/ch9.h>
# include <linux/usb/gadget.h>
2014-04-23 11:56:52 +04:00
# include <linux/usb/phy.h>
# include <linux/usb/otg.h>
# include <linux/usb/otg-fsm.h>
2012-05-11 18:25:46 +04:00
# include "ci.h"
# include "udc.h"
# include "bits.h"
# include "debug.h"
2014-04-23 11:56:40 +04:00
# include "otg.h"
2012-05-11 18:25:46 +04:00
/**
2013-03-30 14:53:51 +04:00
* ci_device_show : prints information about device capabilities and status
2012-05-11 18:25:46 +04:00
*/
2013-03-30 14:53:51 +04:00
static int ci_device_show ( struct seq_file * s , void * data )
2012-05-11 18:25:46 +04:00
{
2013-06-24 15:46:36 +04:00
struct ci_hdrc * ci = s - > private ;
2013-03-30 14:53:51 +04:00
struct usb_gadget * gadget = & ci - > gadget ;
2012-05-11 18:25:46 +04:00
2013-03-30 14:53:51 +04:00
seq_printf ( s , " speed = %d \n " , gadget - > speed ) ;
seq_printf ( s , " max_speed = %d \n " , gadget - > max_speed ) ;
seq_printf ( s , " is_otg = %d \n " , gadget - > is_otg ) ;
seq_printf ( s , " is_a_peripheral = %d \n " , gadget - > is_a_peripheral ) ;
seq_printf ( s , " b_hnp_enable = %d \n " , gadget - > b_hnp_enable ) ;
seq_printf ( s , " a_hnp_support = %d \n " , gadget - > a_hnp_support ) ;
seq_printf ( s , " a_alt_hnp_support = %d \n " , gadget - > a_alt_hnp_support ) ;
seq_printf ( s , " name = %s \n " ,
( gadget - > name ? gadget - > name : " " ) ) ;
if ( ! ci - > driver )
return 0 ;
2012-05-11 18:25:46 +04:00
2013-03-30 14:53:51 +04:00
seq_printf ( s , " gadget function = %s \n " ,
( ci - > driver - > function ? ci - > driver - > function : " " ) ) ;
seq_printf ( s , " gadget max speed = %d \n " , ci - > driver - > max_speed ) ;
2012-05-11 18:25:46 +04:00
return 0 ;
}
2013-03-30 14:53:51 +04:00
static int ci_device_open ( struct inode * inode , struct file * file )
2012-05-11 18:25:46 +04:00
{
2013-03-30 14:53:51 +04:00
return single_open ( file , ci_device_show , inode - > i_private ) ;
2012-05-11 18:25:46 +04:00
}
2013-03-30 14:53:51 +04:00
static const struct file_operations ci_device_fops = {
. open = ci_device_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
2012-05-11 18:25:46 +04:00
/**
2013-03-30 14:53:51 +04:00
* ci_port_test_show : reads port test mode
2012-05-11 18:25:46 +04:00
*/
2013-03-30 14:53:51 +04:00
static int ci_port_test_show ( struct seq_file * s , void * data )
2012-05-11 18:25:46 +04:00
{
2013-06-24 15:46:36 +04:00
struct ci_hdrc * ci = s - > private ;
2012-05-11 18:25:46 +04:00
unsigned long flags ;
unsigned mode ;
2012-07-07 18:56:40 +04:00
spin_lock_irqsave ( & ci - > lock , flags ) ;
mode = hw_port_test_get ( ci ) ;
spin_unlock_irqrestore ( & ci - > lock , flags ) ;
2012-05-11 18:25:46 +04:00
2013-03-30 14:53:51 +04:00
seq_printf ( s , " mode = %u \n " , mode ) ;
return 0 ;
2012-05-11 18:25:46 +04:00
}
/**
2013-03-30 14:53:51 +04:00
* ci_port_test_write : writes port test mode
2012-05-11 18:25:46 +04:00
*/
2013-03-30 14:53:51 +04:00
static ssize_t ci_port_test_write ( struct file * file , const char __user * ubuf ,
size_t count , loff_t * ppos )
2012-05-11 18:25:46 +04:00
{
2013-03-30 14:53:51 +04:00
struct seq_file * s = file - > private_data ;
2013-06-24 15:46:36 +04:00
struct ci_hdrc * ci = s - > private ;
2012-05-11 18:25:46 +04:00
unsigned long flags ;
unsigned mode ;
2013-03-30 14:53:51 +04:00
char buf [ 32 ] ;
int ret ;
2012-05-11 18:25:46 +04:00
2013-03-30 14:53:51 +04:00
if ( copy_from_user ( buf , ubuf , min_t ( size_t , sizeof ( buf ) - 1 , count ) ) )
return - EFAULT ;
2012-05-11 18:25:46 +04:00
2013-03-30 14:53:51 +04:00
if ( sscanf ( buf , " %u " , & mode ) ! = 1 )
return - EINVAL ;
2012-05-11 18:25:46 +04:00
2012-07-07 18:56:40 +04:00
spin_lock_irqsave ( & ci - > lock , flags ) ;
2013-03-30 14:53:51 +04:00
ret = hw_port_test_set ( ci , mode ) ;
2012-07-07 18:56:40 +04:00
spin_unlock_irqrestore ( & ci - > lock , flags ) ;
2012-05-11 18:25:46 +04:00
2013-03-30 14:53:51 +04:00
return ret ? ret : count ;
2012-05-11 18:25:46 +04:00
}
2013-03-30 14:53:51 +04:00
static int ci_port_test_open ( struct inode * inode , struct file * file )
{
return single_open ( file , ci_port_test_show , inode - > i_private ) ;
}
static const struct file_operations ci_port_test_fops = {
. open = ci_port_test_open ,
. write = ci_port_test_write ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
2012-05-11 18:25:46 +04:00
/**
2013-03-30 14:53:51 +04:00
* ci_qheads_show : DMA contents of all queue heads
2012-05-11 18:25:46 +04:00
*/
2013-03-30 14:53:51 +04:00
static int ci_qheads_show ( struct seq_file * s , void * data )
2012-05-11 18:25:46 +04:00
{
2013-06-24 15:46:36 +04:00
struct ci_hdrc * ci = s - > private ;
2012-05-11 18:25:46 +04:00
unsigned long flags ;
2013-03-30 14:53:51 +04:00
unsigned i , j ;
2012-05-11 18:25:46 +04:00
2013-03-30 14:53:51 +04:00
if ( ci - > role ! = CI_ROLE_GADGET ) {
seq_printf ( s , " not in gadget mode \n " ) ;
2012-05-11 18:25:46 +04:00
return 0 ;
}
2012-07-07 18:56:40 +04:00
spin_lock_irqsave ( & ci - > lock , flags ) ;
for ( i = 0 ; i < ci - > hw_ep_max / 2 ; i + + ) {
2013-06-24 15:46:36 +04:00
struct ci_hw_ep * hweprx = & ci - > ci_hw_ep [ i ] ;
struct ci_hw_ep * hweptx =
& ci - > ci_hw_ep [ i + ci - > hw_ep_max / 2 ] ;
2013-03-30 14:53:51 +04:00
seq_printf ( s , " EP=%02i: RX=%08X TX=%08X \n " ,
2013-06-13 19:00:03 +04:00
i , ( u32 ) hweprx - > qh . dma , ( u32 ) hweptx - > qh . dma ) ;
2013-06-24 15:46:36 +04:00
for ( j = 0 ; j < ( sizeof ( struct ci_hw_qh ) / sizeof ( u32 ) ) ; j + + )
2013-03-30 14:53:51 +04:00
seq_printf ( s , " %04X: %08X %08X \n " , j ,
2013-06-13 19:00:03 +04:00
* ( ( u32 * ) hweprx - > qh . ptr + j ) ,
* ( ( u32 * ) hweptx - > qh . ptr + j ) ) ;
2012-05-11 18:25:46 +04:00
}
2012-07-07 18:56:40 +04:00
spin_unlock_irqrestore ( & ci - > lock , flags ) ;
2012-05-11 18:25:46 +04:00
2013-03-30 14:53:51 +04:00
return 0 ;
2012-05-11 18:25:46 +04:00
}
2013-03-30 14:53:51 +04:00
static int ci_qheads_open ( struct inode * inode , struct file * file )
2012-05-11 18:25:46 +04:00
{
2013-03-30 14:53:51 +04:00
return single_open ( file , ci_qheads_show , inode - > i_private ) ;
2012-05-11 18:25:46 +04:00
}
2013-03-30 14:53:51 +04:00
static const struct file_operations ci_qheads_fops = {
. open = ci_qheads_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
2012-05-11 18:25:46 +04:00
/**
2013-03-30 14:53:51 +04:00
* ci_requests_show : DMA contents of all requests currently queued ( all endpts )
2012-05-11 18:25:46 +04:00
*/
2013-03-30 14:53:51 +04:00
static int ci_requests_show ( struct seq_file * s , void * data )
2012-05-11 18:25:46 +04:00
{
2013-06-24 15:46:36 +04:00
struct ci_hdrc * ci = s - > private ;
2012-05-11 18:25:46 +04:00
unsigned long flags ;
struct list_head * ptr = NULL ;
2013-06-24 15:46:36 +04:00
struct ci_hw_req * req = NULL ;
2013-06-13 18:59:53 +04:00
struct td_node * node , * tmpnode ;
2013-06-24 15:46:36 +04:00
unsigned i , j , qsize = sizeof ( struct ci_hw_td ) / sizeof ( u32 ) ;
2012-05-11 18:25:46 +04:00
2013-03-30 14:53:51 +04:00
if ( ci - > role ! = CI_ROLE_GADGET ) {
seq_printf ( s , " not in gadget mode \n " ) ;
2012-05-11 18:25:46 +04:00
return 0 ;
}
2012-07-07 18:56:40 +04:00
spin_lock_irqsave ( & ci - > lock , flags ) ;
for ( i = 0 ; i < ci - > hw_ep_max ; i + + )
2013-06-24 15:46:36 +04:00
list_for_each ( ptr , & ci - > ci_hw_ep [ i ] . qh . queue ) {
req = list_entry ( ptr , struct ci_hw_req , queue ) ;
2012-05-11 18:25:46 +04:00
2013-06-13 18:59:53 +04:00
list_for_each_entry_safe ( node , tmpnode , & req - > tds , td ) {
seq_printf ( s , " EP=%02i: TD=%08X %s \n " ,
i % ( ci - > hw_ep_max / 2 ) ,
( u32 ) node - > dma ,
( ( i < ci - > hw_ep_max / 2 ) ?
" RX " : " TX " ) ) ;
for ( j = 0 ; j < qsize ; j + + )
seq_printf ( s , " %04X: %08X \n " , j ,
* ( ( u32 * ) node - > ptr + j ) ) ;
}
2012-05-11 18:25:46 +04:00
}
2012-07-07 18:56:40 +04:00
spin_unlock_irqrestore ( & ci - > lock , flags ) ;
2012-05-11 18:25:46 +04:00
2013-03-30 14:53:51 +04:00
return 0 ;
}
static int ci_requests_open ( struct inode * inode , struct file * file )
{
return single_open ( file , ci_requests_show , inode - > i_private ) ;
2012-05-11 18:25:46 +04:00
}
2013-03-30 14:53:51 +04:00
static const struct file_operations ci_requests_fops = {
. open = ci_requests_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
2012-05-11 18:25:46 +04:00
2014-07-22 06:09:45 +04:00
static int ci_otg_show ( struct seq_file * s , void * unused )
2014-04-23 11:56:52 +04:00
{
struct ci_hdrc * ci = s - > private ;
struct otg_fsm * fsm ;
if ( ! ci | | ! ci_otg_is_fsm_mode ( ci ) )
return 0 ;
fsm = & ci - > fsm ;
/* ------ State ----- */
seq_printf ( s , " OTG state: %s \n \n " ,
2014-10-30 20:41:16 +03:00
usb_otg_state_string ( ci - > otg . state ) ) ;
2014-04-23 11:56:52 +04:00
/* ------ State Machine Variables ----- */
seq_printf ( s , " a_bus_drop: %d \n " , fsm - > a_bus_drop ) ;
seq_printf ( s , " a_bus_req: %d \n " , fsm - > a_bus_req ) ;
seq_printf ( s , " a_srp_det: %d \n " , fsm - > a_srp_det ) ;
seq_printf ( s , " a_vbus_vld: %d \n " , fsm - > a_vbus_vld ) ;
seq_printf ( s , " b_conn: %d \n " , fsm - > b_conn ) ;
seq_printf ( s , " adp_change: %d \n " , fsm - > adp_change ) ;
seq_printf ( s , " power_up: %d \n " , fsm - > power_up ) ;
seq_printf ( s , " a_bus_resume: %d \n " , fsm - > a_bus_resume ) ;
seq_printf ( s , " a_bus_suspend: %d \n " , fsm - > a_bus_suspend ) ;
seq_printf ( s , " a_conn: %d \n " , fsm - > a_conn ) ;
seq_printf ( s , " b_bus_req: %d \n " , fsm - > b_bus_req ) ;
seq_printf ( s , " b_bus_suspend: %d \n " , fsm - > b_bus_suspend ) ;
seq_printf ( s , " b_se0_srp: %d \n " , fsm - > b_se0_srp ) ;
seq_printf ( s , " b_ssend_srp: %d \n " , fsm - > b_ssend_srp ) ;
seq_printf ( s , " b_sess_vld: %d \n " , fsm - > b_sess_vld ) ;
seq_printf ( s , " b_srp_done: %d \n " , fsm - > b_srp_done ) ;
seq_printf ( s , " drv_vbus: %d \n " , fsm - > drv_vbus ) ;
seq_printf ( s , " loc_conn: %d \n " , fsm - > loc_conn ) ;
seq_printf ( s , " loc_sof: %d \n " , fsm - > loc_sof ) ;
seq_printf ( s , " adp_prb: %d \n " , fsm - > adp_prb ) ;
seq_printf ( s , " id: %d \n " , fsm - > id ) ;
seq_printf ( s , " protocol: %d \n " , fsm - > protocol ) ;
return 0 ;
}
static int ci_otg_open ( struct inode * inode , struct file * file )
{
return single_open ( file , ci_otg_show , inode - > i_private ) ;
}
static const struct file_operations ci_otg_fops = {
. open = ci_otg_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
2013-03-30 14:53:52 +04:00
static int ci_role_show ( struct seq_file * s , void * data )
{
2013-06-24 15:46:36 +04:00
struct ci_hdrc * ci = s - > private ;
2013-03-30 14:53:52 +04:00
seq_printf ( s , " %s \n " , ci_role ( ci ) - > name ) ;
return 0 ;
}
static ssize_t ci_role_write ( struct file * file , const char __user * ubuf ,
size_t count , loff_t * ppos )
{
struct seq_file * s = file - > private_data ;
2013-06-24 15:46:36 +04:00
struct ci_hdrc * ci = s - > private ;
2013-03-30 14:53:52 +04:00
enum ci_role role ;
char buf [ 8 ] ;
int ret ;
if ( copy_from_user ( buf , ubuf , min_t ( size_t , sizeof ( buf ) - 1 , count ) ) )
return - EFAULT ;
for ( role = CI_ROLE_HOST ; role < CI_ROLE_END ; role + + )
if ( ci - > roles [ role ] & &
! strncmp ( buf , ci - > roles [ role ] - > name ,
strlen ( ci - > roles [ role ] - > name ) ) )
break ;
if ( role = = CI_ROLE_END | | role = = ci - > role )
return - EINVAL ;
ci_role_stop ( ci ) ;
ret = ci_role_start ( ci , role ) ;
return ret ? ret : count ;
}
static int ci_role_open ( struct inode * inode , struct file * file )
{
return single_open ( file , ci_role_show , inode - > i_private ) ;
}
static const struct file_operations ci_role_fops = {
. open = ci_role_open ,
. write = ci_role_write ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
2014-07-22 06:09:45 +04:00
static int ci_registers_show ( struct seq_file * s , void * unused )
2014-04-23 11:56:40 +04:00
{
struct ci_hdrc * ci = s - > private ;
u32 tmp_reg ;
if ( ! ci )
return 0 ;
/* ------ Registers ----- */
tmp_reg = hw_read_intr_enable ( ci ) ;
seq_printf ( s , " USBINTR reg: %08x \n " , tmp_reg ) ;
tmp_reg = hw_read_intr_status ( ci ) ;
seq_printf ( s , " USBSTS reg: %08x \n " , tmp_reg ) ;
tmp_reg = hw_read ( ci , OP_USBMODE , ~ 0 ) ;
seq_printf ( s , " USBMODE reg: %08x \n " , tmp_reg ) ;
tmp_reg = hw_read ( ci , OP_USBCMD , ~ 0 ) ;
seq_printf ( s , " USBCMD reg: %08x \n " , tmp_reg ) ;
tmp_reg = hw_read ( ci , OP_PORTSC , ~ 0 ) ;
seq_printf ( s , " PORTSC reg: %08x \n " , tmp_reg ) ;
if ( ci - > is_otg ) {
tmp_reg = hw_read_otgsc ( ci , ~ 0 ) ;
seq_printf ( s , " OTGSC reg: %08x \n " , tmp_reg ) ;
}
return 0 ;
}
static int ci_registers_open ( struct inode * inode , struct file * file )
{
return single_open ( file , ci_registers_show , inode - > i_private ) ;
}
static const struct file_operations ci_registers_fops = {
. open = ci_registers_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
2012-05-11 18:25:46 +04:00
/**
* dbg_create_files : initializes the attribute interface
2013-03-30 14:53:51 +04:00
* @ ci : device
2012-05-11 18:25:46 +04:00
*
* This function returns an error code
*/
2013-06-24 15:46:36 +04:00
int dbg_create_files ( struct ci_hdrc * ci )
2012-05-11 18:25:46 +04:00
{
2013-03-30 14:53:51 +04:00
struct dentry * dent ;
ci - > debugfs = debugfs_create_dir ( dev_name ( ci - > dev ) , NULL ) ;
if ( ! ci - > debugfs )
return - ENOMEM ;
dent = debugfs_create_file ( " device " , S_IRUGO , ci - > debugfs , ci ,
& ci_device_fops ) ;
if ( ! dent )
goto err ;
dent = debugfs_create_file ( " port_test " , S_IRUGO | S_IWUSR , ci - > debugfs ,
ci , & ci_port_test_fops ) ;
if ( ! dent )
goto err ;
dent = debugfs_create_file ( " qheads " , S_IRUGO , ci - > debugfs , ci ,
& ci_qheads_fops ) ;
if ( ! dent )
goto err ;
dent = debugfs_create_file ( " requests " , S_IRUGO , ci - > debugfs , ci ,
& ci_requests_fops ) ;
2013-03-30 14:53:52 +04:00
if ( ! dent )
goto err ;
2014-04-23 11:56:52 +04:00
if ( ci_otg_is_fsm_mode ( ci ) ) {
dent = debugfs_create_file ( " otg " , S_IRUGO , ci - > debugfs , ci ,
& ci_otg_fops ) ;
if ( ! dent )
goto err ;
}
2013-03-30 14:53:52 +04:00
dent = debugfs_create_file ( " role " , S_IRUGO | S_IWUSR , ci - > debugfs , ci ,
& ci_role_fops ) ;
2014-04-23 11:56:40 +04:00
if ( ! dent )
goto err ;
dent = debugfs_create_file ( " registers " , S_IRUGO , ci - > debugfs , ci ,
& ci_registers_fops ) ;
2013-03-30 14:53:51 +04:00
if ( dent )
return 0 ;
err :
debugfs_remove_recursive ( ci - > debugfs ) ;
return - ENOMEM ;
2012-05-11 18:25:46 +04:00
}
/**
* dbg_remove_files : destroys the attribute interface
2013-03-30 14:53:51 +04:00
* @ ci : device
2012-05-11 18:25:46 +04:00
*/
2013-06-24 15:46:36 +04:00
void dbg_remove_files ( struct ci_hdrc * ci )
2012-05-11 18:25:46 +04:00
{
2013-03-30 14:53:51 +04:00
debugfs_remove_recursive ( ci - > debugfs ) ;
2012-05-11 18:25:46 +04:00
}