2011-12-22 13:30:19 +00:00
/*
* Linux network driver for Brocade Converged Network Adapter .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License ( GPL ) Version 2 as
* published by the Free Software Foundation
*
* 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 .
*/
/*
* Copyright ( c ) 2005 - 2011 Brocade Communications Systems , Inc .
* All rights reserved
* www . brocade . com
*/
# include <linux/debugfs.h>
# include <linux/module.h>
# include "bnad.h"
/*
* BNA debufs interface
*
* To access the interface , debugfs file system should be mounted
* if not already mounted using :
* mount - t debugfs none / sys / kernel / debug
*
* BNA Hierarchy :
* - bna / pci_dev : < pci_name >
* where the pci_name corresponds to the one under / sys / bus / pci / drivers / bna
*
* Debugging service available per pci_dev :
* fwtrc : To collect current firmware trace .
* fwsave : To collect last saved fw trace as a result of firmware crash .
* regwr : To write one word to chip register
* regrd : To read one or more words from chip register .
*/
struct bnad_debug_info {
char * debug_buffer ;
void * i_private ;
int buffer_len ;
} ;
static int
bnad_debugfs_open_fwtrc ( struct inode * inode , struct file * file )
{
struct bnad * bnad = inode - > i_private ;
struct bnad_debug_info * fw_debug ;
unsigned long flags ;
int rc ;
fw_debug = kzalloc ( sizeof ( struct bnad_debug_info ) , GFP_KERNEL ) ;
if ( ! fw_debug )
return - ENOMEM ;
fw_debug - > buffer_len = BNA_DBG_FWTRC_LEN ;
fw_debug - > debug_buffer = kzalloc ( fw_debug - > buffer_len , GFP_KERNEL ) ;
if ( ! fw_debug - > debug_buffer ) {
kfree ( fw_debug ) ;
fw_debug = NULL ;
return - ENOMEM ;
}
spin_lock_irqsave ( & bnad - > bna_lock , flags ) ;
rc = bfa_nw_ioc_debug_fwtrc ( & bnad - > bna . ioceth . ioc ,
fw_debug - > debug_buffer ,
& fw_debug - > buffer_len ) ;
spin_unlock_irqrestore ( & bnad - > bna_lock , flags ) ;
if ( rc ! = BFA_STATUS_OK ) {
kfree ( fw_debug - > debug_buffer ) ;
fw_debug - > debug_buffer = NULL ;
kfree ( fw_debug ) ;
fw_debug = NULL ;
pr_warn ( " bnad %s: Failed to collect fwtrc \n " ,
pci_name ( bnad - > pcidev ) ) ;
return - ENOMEM ;
}
file - > private_data = fw_debug ;
return 0 ;
}
static int
bnad_debugfs_open_fwsave ( struct inode * inode , struct file * file )
{
struct bnad * bnad = inode - > i_private ;
struct bnad_debug_info * fw_debug ;
unsigned long flags ;
int rc ;
fw_debug = kzalloc ( sizeof ( struct bnad_debug_info ) , GFP_KERNEL ) ;
if ( ! fw_debug )
return - ENOMEM ;
fw_debug - > buffer_len = BNA_DBG_FWTRC_LEN ;
fw_debug - > debug_buffer = kzalloc ( fw_debug - > buffer_len , GFP_KERNEL ) ;
if ( ! fw_debug - > debug_buffer ) {
kfree ( fw_debug ) ;
fw_debug = NULL ;
return - ENOMEM ;
}
spin_lock_irqsave ( & bnad - > bna_lock , flags ) ;
rc = bfa_nw_ioc_debug_fwsave ( & bnad - > bna . ioceth . ioc ,
fw_debug - > debug_buffer ,
& fw_debug - > buffer_len ) ;
spin_unlock_irqrestore ( & bnad - > bna_lock , flags ) ;
if ( rc ! = BFA_STATUS_OK & & rc ! = BFA_STATUS_ENOFSAVE ) {
kfree ( fw_debug - > debug_buffer ) ;
fw_debug - > debug_buffer = NULL ;
kfree ( fw_debug ) ;
fw_debug = NULL ;
pr_warn ( " bna %s: Failed to collect fwsave \n " ,
pci_name ( bnad - > pcidev ) ) ;
return - ENOMEM ;
}
file - > private_data = fw_debug ;
return 0 ;
}
static int
bnad_debugfs_open_reg ( struct inode * inode , struct file * file )
{
struct bnad_debug_info * reg_debug ;
reg_debug = kzalloc ( sizeof ( struct bnad_debug_info ) , GFP_KERNEL ) ;
if ( ! reg_debug )
return - ENOMEM ;
reg_debug - > i_private = inode - > i_private ;
file - > private_data = reg_debug ;
return 0 ;
}
static int
bnad_get_debug_drvinfo ( struct bnad * bnad , void * buffer , u32 len )
{
struct bnad_drvinfo * drvinfo = ( struct bnad_drvinfo * ) buffer ;
struct bnad_iocmd_comp fcomp ;
unsigned long flags = 0 ;
int ret = BFA_STATUS_FAILED ;
/* Get IOC info */
spin_lock_irqsave ( & bnad - > bna_lock , flags ) ;
bfa_nw_ioc_get_attr ( & bnad - > bna . ioceth . ioc , & drvinfo - > ioc_attr ) ;
spin_unlock_irqrestore ( & bnad - > bna_lock , flags ) ;
/* Retrieve CEE related info */
fcomp . bnad = bnad ;
fcomp . comp_status = 0 ;
init_completion ( & fcomp . comp ) ;
spin_lock_irqsave ( & bnad - > bna_lock , flags ) ;
ret = bfa_nw_cee_get_attr ( & bnad - > bna . cee , & drvinfo - > cee_attr ,
bnad_cb_completion , & fcomp ) ;
if ( ret ! = BFA_STATUS_OK ) {
spin_unlock_irqrestore ( & bnad - > bna_lock , flags ) ;
goto out ;
}
spin_unlock_irqrestore ( & bnad - > bna_lock , flags ) ;
wait_for_completion ( & fcomp . comp ) ;
drvinfo - > cee_status = fcomp . comp_status ;
/* Retrieve flash partition info */
fcomp . comp_status = 0 ;
init_completion ( & fcomp . comp ) ;
spin_lock_irqsave ( & bnad - > bna_lock , flags ) ;
ret = bfa_nw_flash_get_attr ( & bnad - > bna . flash , & drvinfo - > flash_attr ,
bnad_cb_completion , & fcomp ) ;
if ( ret ! = BFA_STATUS_OK ) {
spin_unlock_irqrestore ( & bnad - > bna_lock , flags ) ;
goto out ;
}
spin_unlock_irqrestore ( & bnad - > bna_lock , flags ) ;
wait_for_completion ( & fcomp . comp ) ;
drvinfo - > flash_status = fcomp . comp_status ;
out :
return ret ;
}
static int
bnad_debugfs_open_drvinfo ( struct inode * inode , struct file * file )
{
struct bnad * bnad = inode - > i_private ;
struct bnad_debug_info * drv_info ;
int rc ;
drv_info = kzalloc ( sizeof ( struct bnad_debug_info ) , GFP_KERNEL ) ;
if ( ! drv_info )
return - ENOMEM ;
drv_info - > buffer_len = sizeof ( struct bnad_drvinfo ) ;
drv_info - > debug_buffer = kzalloc ( drv_info - > buffer_len , GFP_KERNEL ) ;
if ( ! drv_info - > debug_buffer ) {
kfree ( drv_info ) ;
drv_info = NULL ;
return - ENOMEM ;
}
mutex_lock ( & bnad - > conf_mutex ) ;
rc = bnad_get_debug_drvinfo ( bnad , drv_info - > debug_buffer ,
drv_info - > buffer_len ) ;
mutex_unlock ( & bnad - > conf_mutex ) ;
if ( rc ! = BFA_STATUS_OK ) {
kfree ( drv_info - > debug_buffer ) ;
drv_info - > debug_buffer = NULL ;
kfree ( drv_info ) ;
drv_info = NULL ;
pr_warn ( " bna %s: Failed to collect drvinfo \n " ,
pci_name ( bnad - > pcidev ) ) ;
return - ENOMEM ;
}
file - > private_data = drv_info ;
return 0 ;
}
/* Changes the current file position */
static loff_t
bnad_debugfs_lseek ( struct file * file , loff_t offset , int orig )
{
loff_t pos = file - > f_pos ;
struct bnad_debug_info * debug = file - > private_data ;
if ( ! debug )
return - EINVAL ;
switch ( orig ) {
case 0 :
file - > f_pos = offset ;
break ;
case 1 :
file - > f_pos + = offset ;
break ;
case 2 :
file - > f_pos = debug - > buffer_len - offset ;
break ;
default :
return - EINVAL ;
}
if ( file - > f_pos < 0 | | file - > f_pos > debug - > buffer_len ) {
file - > f_pos = pos ;
return - EINVAL ;
}
return file - > f_pos ;
}
static ssize_t
bnad_debugfs_read ( struct file * file , char __user * buf ,
size_t nbytes , loff_t * pos )
{
struct bnad_debug_info * debug = file - > private_data ;
if ( ! debug | | ! debug - > debug_buffer )
return 0 ;
return simple_read_from_buffer ( buf , nbytes , pos ,
debug - > debug_buffer , debug - > buffer_len ) ;
}
# define BFA_REG_CT_ADDRSZ (0x40000)
# define BFA_REG_CB_ADDRSZ (0x20000)
# define BFA_REG_ADDRSZ(__ioc) \
( ( u32 ) ( bfa_asic_id_ctc ( bfa_ioc_devid ( __ioc ) ) ? \
BFA_REG_CT_ADDRSZ : BFA_REG_CB_ADDRSZ ) )
# define BFA_REG_ADDRMSK(__ioc) (BFA_REG_ADDRSZ(__ioc) - 1)
/*
* Function to check if the register offset passed is valid .
*/
static int
bna_reg_offset_check ( struct bfa_ioc * ioc , u32 offset , u32 len )
{
u8 area ;
/* check [16:15] */
area = ( offset > > 15 ) & 0x7 ;
if ( area = = 0 ) {
/* PCIe core register */
if ( ( offset + ( len < < 2 ) ) > 0x8000 ) /* 8k dwords or 32KB */
return BFA_STATUS_EINVAL ;
} else if ( area = = 0x1 ) {
/* CB 32 KB memory page */
if ( ( offset + ( len < < 2 ) ) > 0x10000 ) /* 8k dwords or 32KB */
return BFA_STATUS_EINVAL ;
} else {
/* CB register space 64KB */
if ( ( offset + ( len < < 2 ) ) > BFA_REG_ADDRMSK ( ioc ) )
return BFA_STATUS_EINVAL ;
}
return BFA_STATUS_OK ;
}
static ssize_t
bnad_debugfs_read_regrd ( struct file * file , char __user * buf ,
size_t nbytes , loff_t * pos )
{
struct bnad_debug_info * regrd_debug = file - > private_data ;
struct bnad * bnad = ( struct bnad * ) regrd_debug - > i_private ;
ssize_t rc ;
if ( ! bnad - > regdata )
return 0 ;
rc = simple_read_from_buffer ( buf , nbytes , pos ,
bnad - > regdata , bnad - > reglen ) ;
if ( ( * pos + nbytes ) > = bnad - > reglen ) {
kfree ( bnad - > regdata ) ;
bnad - > regdata = NULL ;
bnad - > reglen = 0 ;
}
return rc ;
}
static ssize_t
bnad_debugfs_write_regrd ( struct file * file , const char __user * buf ,
size_t nbytes , loff_t * ppos )
{
struct bnad_debug_info * regrd_debug = file - > private_data ;
struct bnad * bnad = ( struct bnad * ) regrd_debug - > i_private ;
struct bfa_ioc * ioc = & bnad - > bna . ioceth . ioc ;
int addr , len , rc , i ;
u32 * regbuf ;
void __iomem * rb , * reg_addr ;
unsigned long flags ;
void * kern_buf ;
/* Allocate memory to store the user space buf */
kern_buf = kzalloc ( nbytes , GFP_KERNEL ) ;
2012-01-29 12:56:23 +00:00
if ( ! kern_buf )
2011-12-22 13:30:19 +00:00
return - ENOMEM ;
if ( copy_from_user ( kern_buf , ( void __user * ) buf , nbytes ) ) {
kfree ( kern_buf ) ;
return - ENOMEM ;
}
rc = sscanf ( kern_buf , " %x:%x " , & addr , & len ) ;
if ( rc < 2 ) {
pr_warn ( " bna %s: Failed to read user buffer \n " ,
pci_name ( bnad - > pcidev ) ) ;
kfree ( kern_buf ) ;
return - EINVAL ;
}
kfree ( kern_buf ) ;
kfree ( bnad - > regdata ) ;
bnad - > regdata = NULL ;
bnad - > reglen = 0 ;
bnad - > regdata = kzalloc ( len < < 2 , GFP_KERNEL ) ;
2012-01-29 12:56:23 +00:00
if ( ! bnad - > regdata )
2011-12-22 13:30:19 +00:00
return - ENOMEM ;
bnad - > reglen = len < < 2 ;
rb = bfa_ioc_bar0 ( ioc ) ;
addr & = BFA_REG_ADDRMSK ( ioc ) ;
/* offset and len sanity check */
rc = bna_reg_offset_check ( ioc , addr , len ) ;
if ( rc ) {
pr_warn ( " bna %s: Failed reg offset check \n " ,
pci_name ( bnad - > pcidev ) ) ;
kfree ( bnad - > regdata ) ;
bnad - > regdata = NULL ;
bnad - > reglen = 0 ;
return - EINVAL ;
}
reg_addr = rb + addr ;
regbuf = ( u32 * ) bnad - > regdata ;
spin_lock_irqsave ( & bnad - > bna_lock , flags ) ;
for ( i = 0 ; i < len ; i + + ) {
* regbuf = readl ( reg_addr ) ;
regbuf + + ;
reg_addr + = sizeof ( u32 ) ;
}
spin_unlock_irqrestore ( & bnad - > bna_lock , flags ) ;
return nbytes ;
}
static ssize_t
bnad_debugfs_write_regwr ( struct file * file , const char __user * buf ,
size_t nbytes , loff_t * ppos )
{
struct bnad_debug_info * debug = file - > private_data ;
struct bnad * bnad = ( struct bnad * ) debug - > i_private ;
struct bfa_ioc * ioc = & bnad - > bna . ioceth . ioc ;
int addr , val , rc ;
void __iomem * reg_addr ;
unsigned long flags ;
void * kern_buf ;
/* Allocate memory to store the user space buf */
kern_buf = kzalloc ( nbytes , GFP_KERNEL ) ;
2012-01-29 12:56:23 +00:00
if ( ! kern_buf )
2011-12-22 13:30:19 +00:00
return - ENOMEM ;
if ( copy_from_user ( kern_buf , ( void __user * ) buf , nbytes ) ) {
kfree ( kern_buf ) ;
return - ENOMEM ;
}
rc = sscanf ( kern_buf , " %x:%x " , & addr , & val ) ;
if ( rc < 2 ) {
pr_warn ( " bna %s: Failed to read user buffer \n " ,
pci_name ( bnad - > pcidev ) ) ;
kfree ( kern_buf ) ;
return - EINVAL ;
}
kfree ( kern_buf ) ;
addr & = BFA_REG_ADDRMSK ( ioc ) ; /* offset only 17 bit and word align */
/* offset and len sanity check */
rc = bna_reg_offset_check ( ioc , addr , 1 ) ;
if ( rc ) {
pr_warn ( " bna %s: Failed reg offset check \n " ,
pci_name ( bnad - > pcidev ) ) ;
return - EINVAL ;
}
reg_addr = ( bfa_ioc_bar0 ( ioc ) ) + addr ;
spin_lock_irqsave ( & bnad - > bna_lock , flags ) ;
writel ( val , reg_addr ) ;
spin_unlock_irqrestore ( & bnad - > bna_lock , flags ) ;
return nbytes ;
}
static int
bnad_debugfs_release ( struct inode * inode , struct file * file )
{
struct bnad_debug_info * debug = file - > private_data ;
if ( ! debug )
return 0 ;
file - > private_data = NULL ;
kfree ( debug ) ;
return 0 ;
}
static int
bnad_debugfs_buffer_release ( struct inode * inode , struct file * file )
{
struct bnad_debug_info * debug = file - > private_data ;
if ( ! debug )
return 0 ;
kfree ( debug - > debug_buffer ) ;
file - > private_data = NULL ;
kfree ( debug ) ;
debug = NULL ;
return 0 ;
}
static const struct file_operations bnad_debugfs_op_fwtrc = {
. owner = THIS_MODULE ,
. open = bnad_debugfs_open_fwtrc ,
. llseek = bnad_debugfs_lseek ,
. read = bnad_debugfs_read ,
. release = bnad_debugfs_buffer_release ,
} ;
static const struct file_operations bnad_debugfs_op_fwsave = {
. owner = THIS_MODULE ,
. open = bnad_debugfs_open_fwsave ,
. llseek = bnad_debugfs_lseek ,
. read = bnad_debugfs_read ,
. release = bnad_debugfs_buffer_release ,
} ;
static const struct file_operations bnad_debugfs_op_regrd = {
. owner = THIS_MODULE ,
. open = bnad_debugfs_open_reg ,
. llseek = bnad_debugfs_lseek ,
. read = bnad_debugfs_read_regrd ,
. write = bnad_debugfs_write_regrd ,
. release = bnad_debugfs_release ,
} ;
static const struct file_operations bnad_debugfs_op_regwr = {
. owner = THIS_MODULE ,
. open = bnad_debugfs_open_reg ,
. llseek = bnad_debugfs_lseek ,
. write = bnad_debugfs_write_regwr ,
. release = bnad_debugfs_release ,
} ;
static const struct file_operations bnad_debugfs_op_drvinfo = {
. owner = THIS_MODULE ,
. open = bnad_debugfs_open_drvinfo ,
. llseek = bnad_debugfs_lseek ,
. read = bnad_debugfs_read ,
. release = bnad_debugfs_buffer_release ,
} ;
struct bnad_debugfs_entry {
const char * name ;
2012-03-20 06:00:24 -04:00
umode_t mode ;
2011-12-22 13:30:19 +00:00
const struct file_operations * fops ;
} ;
static const struct bnad_debugfs_entry bnad_debugfs_files [ ] = {
{ " fwtrc " , S_IFREG | S_IRUGO , & bnad_debugfs_op_fwtrc , } ,
{ " fwsave " , S_IFREG | S_IRUGO , & bnad_debugfs_op_fwsave , } ,
{ " regrd " , S_IFREG | S_IRUGO | S_IWUSR , & bnad_debugfs_op_regrd , } ,
{ " regwr " , S_IFREG | S_IWUSR , & bnad_debugfs_op_regwr , } ,
{ " drvinfo " , S_IFREG | S_IRUGO , & bnad_debugfs_op_drvinfo , } ,
} ;
static struct dentry * bna_debugfs_root ;
static atomic_t bna_debugfs_port_count ;
/* Initialize debugfs interface for BNA */
void
bnad_debugfs_init ( struct bnad * bnad )
{
const struct bnad_debugfs_entry * file ;
char name [ 64 ] ;
int i ;
/* Setup the BNA debugfs root directory*/
if ( ! bna_debugfs_root ) {
bna_debugfs_root = debugfs_create_dir ( " bna " , NULL ) ;
atomic_set ( & bna_debugfs_port_count , 0 ) ;
if ( ! bna_debugfs_root ) {
pr_warn ( " BNA: debugfs root dir creation failed \n " ) ;
return ;
}
}
/* Setup the pci_dev debugfs directory for the port */
snprintf ( name , sizeof ( name ) , " pci_dev:%s " , pci_name ( bnad - > pcidev ) ) ;
if ( ! bnad - > port_debugfs_root ) {
bnad - > port_debugfs_root =
debugfs_create_dir ( name , bna_debugfs_root ) ;
if ( ! bnad - > port_debugfs_root ) {
pr_warn ( " bna pci_dev %s: root dir creation failed \n " ,
pci_name ( bnad - > pcidev ) ) ;
return ;
}
atomic_inc ( & bna_debugfs_port_count ) ;
for ( i = 0 ; i < ARRAY_SIZE ( bnad_debugfs_files ) ; i + + ) {
file = & bnad_debugfs_files [ i ] ;
bnad - > bnad_dentry_files [ i ] =
debugfs_create_file ( file - > name ,
file - > mode ,
bnad - > port_debugfs_root ,
bnad ,
file - > fops ) ;
if ( ! bnad - > bnad_dentry_files [ i ] ) {
pr_warn (
" BNA pci_dev:%s: create %s entry failed \n " ,
pci_name ( bnad - > pcidev ) , file - > name ) ;
return ;
}
}
}
}
/* Uninitialize debugfs interface for BNA */
void
bnad_debugfs_uninit ( struct bnad * bnad )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( bnad_debugfs_files ) ; i + + ) {
if ( bnad - > bnad_dentry_files [ i ] ) {
debugfs_remove ( bnad - > bnad_dentry_files [ i ] ) ;
bnad - > bnad_dentry_files [ i ] = NULL ;
}
}
/* Remove the pci_dev debugfs directory for the port */
if ( bnad - > port_debugfs_root ) {
debugfs_remove ( bnad - > port_debugfs_root ) ;
bnad - > port_debugfs_root = NULL ;
atomic_dec ( & bna_debugfs_port_count ) ;
}
/* Remove the BNA debugfs root directory */
if ( atomic_read ( & bna_debugfs_port_count ) = = 0 ) {
debugfs_remove ( bna_debugfs_root ) ;
bna_debugfs_root = NULL ;
}
}