2010-07-09 07:02:55 +04:00
/*
* Copyright ( c ) 2005 - 2010 Brocade Communications Systems , Inc .
* All rights reserved
* www . brocade . com
*
* Linux driver for Brocade Fibre Channel Host Bus 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 .
*/
# include <linux/debugfs.h>
2011-05-27 17:37:25 +04:00
# include <linux/export.h>
2010-07-09 07:02:55 +04:00
2010-09-15 22:50:55 +04:00
# include "bfad_drv.h"
# include "bfad_im.h"
2010-07-09 07:02:55 +04:00
/*
* BFA debufs interface
*
* To access the interface , debugfs file system should be mounted
* if not already mounted using :
* mount - t debugfs none / sys / kernel / debug
*
* BFA Hierarchy :
2011-04-15 03:50:35 +04:00
* - bfa / pci_dev : < pci_name >
* where the pci_name corresponds to the one under / sys / bus / pci / drivers / bfa
2010-07-09 07:02:55 +04:00
*
2011-04-15 03:50:35 +04:00
* Debugging service available per pci_dev :
2010-07-09 07:02:55 +04:00
* fwtrc : To collect current firmware trace .
* drvtrc : To collect current driver 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 bfad_debug_info {
char * debug_buffer ;
void * i_private ;
int buffer_len ;
} ;
static int
bfad_debugfs_open_drvtrc ( struct inode * inode , struct file * file )
{
struct bfad_port_s * port = inode - > i_private ;
struct bfad_s * bfad = port - > bfad ;
struct bfad_debug_info * debug ;
debug = kzalloc ( sizeof ( struct bfad_debug_info ) , GFP_KERNEL ) ;
if ( ! debug )
return - ENOMEM ;
debug - > debug_buffer = ( void * ) bfad - > trcmod ;
debug - > buffer_len = sizeof ( struct bfa_trc_mod_s ) ;
file - > private_data = debug ;
return 0 ;
}
static int
bfad_debugfs_open_fwtrc ( struct inode * inode , struct file * file )
{
struct bfad_port_s * port = inode - > i_private ;
struct bfad_s * bfad = port - > bfad ;
struct bfad_debug_info * fw_debug ;
unsigned long flags ;
int rc ;
fw_debug = kzalloc ( sizeof ( struct bfad_debug_info ) , GFP_KERNEL ) ;
if ( ! fw_debug )
return - ENOMEM ;
fw_debug - > buffer_len = sizeof ( struct bfa_trc_mod_s ) ;
fw_debug - > debug_buffer = vmalloc ( fw_debug - > buffer_len ) ;
if ( ! fw_debug - > debug_buffer ) {
kfree ( fw_debug ) ;
printk ( KERN_INFO " bfad[%d]: Failed to allocate fwtrc buffer \n " ,
bfad - > inst_no ) ;
return - ENOMEM ;
}
memset ( fw_debug - > debug_buffer , 0 , fw_debug - > buffer_len ) ;
spin_lock_irqsave ( & bfad - > bfad_lock , flags ) ;
2010-12-10 06:08:43 +03:00
rc = bfa_ioc_debug_fwtrc ( & bfad - > bfa . ioc ,
2010-07-09 07:02:55 +04:00
fw_debug - > debug_buffer ,
& fw_debug - > buffer_len ) ;
spin_unlock_irqrestore ( & bfad - > bfad_lock , flags ) ;
if ( rc ! = BFA_STATUS_OK ) {
vfree ( fw_debug - > debug_buffer ) ;
fw_debug - > debug_buffer = NULL ;
kfree ( fw_debug ) ;
printk ( KERN_INFO " bfad[%d]: Failed to collect fwtrc \n " ,
bfad - > inst_no ) ;
return - ENOMEM ;
}
file - > private_data = fw_debug ;
return 0 ;
}
static int
bfad_debugfs_open_fwsave ( struct inode * inode , struct file * file )
{
struct bfad_port_s * port = inode - > i_private ;
struct bfad_s * bfad = port - > bfad ;
struct bfad_debug_info * fw_debug ;
unsigned long flags ;
int rc ;
fw_debug = kzalloc ( sizeof ( struct bfad_debug_info ) , GFP_KERNEL ) ;
if ( ! fw_debug )
return - ENOMEM ;
fw_debug - > buffer_len = sizeof ( struct bfa_trc_mod_s ) ;
fw_debug - > debug_buffer = vmalloc ( fw_debug - > buffer_len ) ;
if ( ! fw_debug - > debug_buffer ) {
kfree ( fw_debug ) ;
printk ( KERN_INFO " bfad[%d]: Failed to allocate fwsave buffer \n " ,
bfad - > inst_no ) ;
return - ENOMEM ;
}
memset ( fw_debug - > debug_buffer , 0 , fw_debug - > buffer_len ) ;
spin_lock_irqsave ( & bfad - > bfad_lock , flags ) ;
2010-12-10 06:08:43 +03:00
rc = bfa_ioc_debug_fwsave ( & bfad - > bfa . ioc ,
2010-07-09 07:02:55 +04:00
fw_debug - > debug_buffer ,
& fw_debug - > buffer_len ) ;
spin_unlock_irqrestore ( & bfad - > bfad_lock , flags ) ;
if ( rc ! = BFA_STATUS_OK ) {
vfree ( fw_debug - > debug_buffer ) ;
fw_debug - > debug_buffer = NULL ;
kfree ( fw_debug ) ;
printk ( KERN_INFO " bfad[%d]: Failed to collect fwsave \n " ,
bfad - > inst_no ) ;
return - ENOMEM ;
}
file - > private_data = fw_debug ;
return 0 ;
}
static int
bfad_debugfs_open_reg ( struct inode * inode , struct file * file )
{
struct bfad_debug_info * reg_debug ;
reg_debug = kzalloc ( sizeof ( struct bfad_debug_info ) , GFP_KERNEL ) ;
if ( ! reg_debug )
return - ENOMEM ;
reg_debug - > i_private = inode - > i_private ;
file - > private_data = reg_debug ;
return 0 ;
}
/* Changes the current file position */
static loff_t
bfad_debugfs_lseek ( struct file * file , loff_t offset , int orig )
{
2013-06-17 17:45:46 +04:00
struct bfad_debug_info * debug = file - > private_data ;
return fixed_size_llseek ( file , offset , orig ,
debug - > buffer_len ) ;
2010-07-09 07:02:55 +04:00
}
static ssize_t
bfad_debugfs_read ( struct file * file , char __user * buf ,
size_t nbytes , loff_t * pos )
{
struct bfad_debug_info * debug = file - > private_data ;
if ( ! debug | | ! debug - > debug_buffer )
return 0 ;
2010-11-30 05:21:32 +03:00
return simple_read_from_buffer ( buf , nbytes , pos ,
2010-07-09 07:02:55 +04:00
debug - > debug_buffer , debug - > buffer_len ) ;
}
# define BFA_REG_CT_ADDRSZ (0x40000)
# define BFA_REG_CB_ADDRSZ (0x20000)
2011-06-25 07:28:17 +04:00
# 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)
2010-07-09 07:02:55 +04:00
static bfa_status_t
bfad_reg_offset_check ( struct bfa_s * bfa , 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 */
2011-06-25 07:28:17 +04:00
if ( ( offset + ( len < < 2 ) ) > BFA_REG_ADDRMSK ( & bfa - > ioc ) )
2010-07-09 07:02:55 +04:00
return BFA_STATUS_EINVAL ;
}
return BFA_STATUS_OK ;
}
static ssize_t
bfad_debugfs_read_regrd ( struct file * file , char __user * buf ,
size_t nbytes , loff_t * pos )
{
struct bfad_debug_info * regrd_debug = file - > private_data ;
struct bfad_port_s * port = ( struct bfad_port_s * ) regrd_debug - > i_private ;
struct bfad_s * bfad = port - > bfad ;
ssize_t rc ;
if ( ! bfad - > regdata )
return 0 ;
2010-11-30 05:21:32 +03:00
rc = simple_read_from_buffer ( buf , nbytes , pos ,
2010-07-09 07:02:55 +04:00
bfad - > regdata , bfad - > reglen ) ;
if ( ( * pos + nbytes ) > = bfad - > reglen ) {
kfree ( bfad - > regdata ) ;
bfad - > regdata = NULL ;
bfad - > reglen = 0 ;
}
return rc ;
}
static ssize_t
bfad_debugfs_write_regrd ( struct file * file , const char __user * buf ,
size_t nbytes , loff_t * ppos )
{
struct bfad_debug_info * regrd_debug = file - > private_data ;
struct bfad_port_s * port = ( struct bfad_port_s * ) regrd_debug - > i_private ;
struct bfad_s * bfad = port - > bfad ;
struct bfa_s * bfa = & bfad - > bfa ;
struct bfa_ioc_s * ioc = & bfa - > ioc ;
int addr , len , rc , i ;
u32 * regbuf ;
void __iomem * rb , * reg_addr ;
unsigned long flags ;
2010-11-30 05:21:32 +03:00
void * kern_buf ;
2010-07-09 07:02:55 +04:00
2010-11-30 05:21:32 +03:00
kern_buf = kzalloc ( nbytes , GFP_KERNEL ) ;
if ( ! kern_buf ) {
printk ( KERN_INFO " bfad[%d]: Failed to allocate buffer \n " ,
bfad - > inst_no ) ;
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 ) ;
2010-07-09 07:02:55 +04:00
if ( rc < 2 ) {
printk ( KERN_INFO
" bfad[%d]: %s failed to read user buf \n " ,
bfad - > inst_no , __func__ ) ;
2010-11-30 05:21:32 +03:00
kfree ( kern_buf ) ;
2010-07-09 07:02:55 +04:00
return - EINVAL ;
}
2010-11-30 05:21:32 +03:00
kfree ( kern_buf ) ;
2010-07-09 07:02:55 +04:00
kfree ( bfad - > regdata ) ;
bfad - > regdata = NULL ;
bfad - > reglen = 0 ;
bfad - > regdata = kzalloc ( len < < 2 , GFP_KERNEL ) ;
if ( ! bfad - > regdata ) {
printk ( KERN_INFO " bfad[%d]: Failed to allocate regrd buffer \n " ,
bfad - > inst_no ) ;
return - ENOMEM ;
}
bfad - > reglen = len < < 2 ;
rb = bfa_ioc_bar0 ( ioc ) ;
2011-06-25 07:28:17 +04:00
addr & = BFA_REG_ADDRMSK ( ioc ) ;
2010-07-09 07:02:55 +04:00
/* offset and len sanity check */
rc = bfad_reg_offset_check ( bfa , addr , len ) ;
if ( rc ) {
printk ( KERN_INFO " bfad[%d]: Failed reg offset check \n " ,
bfad - > inst_no ) ;
kfree ( bfad - > regdata ) ;
bfad - > regdata = NULL ;
bfad - > reglen = 0 ;
return - EINVAL ;
}
reg_addr = rb + addr ;
regbuf = ( u32 * ) bfad - > regdata ;
spin_lock_irqsave ( & bfad - > bfad_lock , flags ) ;
for ( i = 0 ; i < len ; i + + ) {
2010-10-19 04:12:29 +04:00
* regbuf = readl ( reg_addr ) ;
2010-07-09 07:02:55 +04:00
regbuf + + ;
reg_addr + = sizeof ( u32 ) ;
}
spin_unlock_irqrestore ( & bfad - > bfad_lock , flags ) ;
return nbytes ;
}
static ssize_t
bfad_debugfs_write_regwr ( struct file * file , const char __user * buf ,
size_t nbytes , loff_t * ppos )
{
struct bfad_debug_info * debug = file - > private_data ;
struct bfad_port_s * port = ( struct bfad_port_s * ) debug - > i_private ;
struct bfad_s * bfad = port - > bfad ;
struct bfa_s * bfa = & bfad - > bfa ;
struct bfa_ioc_s * ioc = & bfa - > ioc ;
int addr , val , rc ;
void __iomem * reg_addr ;
unsigned long flags ;
2010-11-30 05:21:32 +03:00
void * kern_buf ;
kern_buf = kzalloc ( nbytes , GFP_KERNEL ) ;
if ( ! kern_buf ) {
printk ( KERN_INFO " bfad[%d]: Failed to allocate buffer \n " ,
bfad - > inst_no ) ;
return - ENOMEM ;
}
if ( copy_from_user ( kern_buf , ( void __user * ) buf , nbytes ) ) {
kfree ( kern_buf ) ;
return - ENOMEM ;
}
2010-07-09 07:02:55 +04:00
2010-11-30 05:21:32 +03:00
rc = sscanf ( kern_buf , " %x:%x " , & addr , & val ) ;
2010-07-09 07:02:55 +04:00
if ( rc < 2 ) {
printk ( KERN_INFO
" bfad[%d]: %s failed to read user buf \n " ,
bfad - > inst_no , __func__ ) ;
2010-11-30 05:21:32 +03:00
kfree ( kern_buf ) ;
2010-07-09 07:02:55 +04:00
return - EINVAL ;
}
2010-11-30 05:21:32 +03:00
kfree ( kern_buf ) ;
2010-07-09 07:02:55 +04:00
2011-06-25 07:28:17 +04:00
addr & = BFA_REG_ADDRMSK ( ioc ) ; /* offset only 17 bit and word align */
2010-07-09 07:02:55 +04:00
/* offset and len sanity check */
rc = bfad_reg_offset_check ( bfa , addr , 1 ) ;
if ( rc ) {
printk ( KERN_INFO
" bfad[%d]: Failed reg offset check \n " ,
bfad - > inst_no ) ;
return - EINVAL ;
}
2010-11-30 05:21:32 +03:00
reg_addr = ( bfa_ioc_bar0 ( ioc ) ) + addr ;
2010-07-09 07:02:55 +04:00
spin_lock_irqsave ( & bfad - > bfad_lock , flags ) ;
2010-10-19 04:12:29 +04:00
writel ( val , reg_addr ) ;
2010-07-09 07:02:55 +04:00
spin_unlock_irqrestore ( & bfad - > bfad_lock , flags ) ;
return nbytes ;
}
static int
bfad_debugfs_release ( struct inode * inode , struct file * file )
{
struct bfad_debug_info * debug = file - > private_data ;
if ( ! debug )
return 0 ;
file - > private_data = NULL ;
kfree ( debug ) ;
return 0 ;
}
static int
bfad_debugfs_release_fwtrc ( struct inode * inode , struct file * file )
{
struct bfad_debug_info * fw_debug = file - > private_data ;
if ( ! fw_debug )
return 0 ;
if ( fw_debug - > debug_buffer )
vfree ( fw_debug - > debug_buffer ) ;
file - > private_data = NULL ;
kfree ( fw_debug ) ;
return 0 ;
}
static const struct file_operations bfad_debugfs_op_drvtrc = {
. owner = THIS_MODULE ,
. open = bfad_debugfs_open_drvtrc ,
. llseek = bfad_debugfs_lseek ,
. read = bfad_debugfs_read ,
. release = bfad_debugfs_release ,
} ;
static const struct file_operations bfad_debugfs_op_fwtrc = {
. owner = THIS_MODULE ,
. open = bfad_debugfs_open_fwtrc ,
. llseek = bfad_debugfs_lseek ,
. read = bfad_debugfs_read ,
. release = bfad_debugfs_release_fwtrc ,
} ;
static const struct file_operations bfad_debugfs_op_fwsave = {
. owner = THIS_MODULE ,
. open = bfad_debugfs_open_fwsave ,
. llseek = bfad_debugfs_lseek ,
. read = bfad_debugfs_read ,
. release = bfad_debugfs_release_fwtrc ,
} ;
static const struct file_operations bfad_debugfs_op_regrd = {
. owner = THIS_MODULE ,
. open = bfad_debugfs_open_reg ,
. llseek = bfad_debugfs_lseek ,
. read = bfad_debugfs_read_regrd ,
. write = bfad_debugfs_write_regrd ,
. release = bfad_debugfs_release ,
} ;
static const struct file_operations bfad_debugfs_op_regwr = {
. owner = THIS_MODULE ,
. open = bfad_debugfs_open_reg ,
. llseek = bfad_debugfs_lseek ,
. write = bfad_debugfs_write_regwr ,
. release = bfad_debugfs_release ,
} ;
struct bfad_debugfs_entry {
const char * name ;
2011-07-24 12:33:43 +04:00
umode_t mode ;
2010-07-09 07:02:55 +04:00
const struct file_operations * fops ;
} ;
static const struct bfad_debugfs_entry bfad_debugfs_files [ ] = {
{ " drvtrc " , S_IFREG | S_IRUGO , & bfad_debugfs_op_drvtrc , } ,
{ " fwtrc " , S_IFREG | S_IRUGO , & bfad_debugfs_op_fwtrc , } ,
{ " fwsave " , S_IFREG | S_IRUGO , & bfad_debugfs_op_fwsave , } ,
{ " regrd " , S_IFREG | S_IRUGO | S_IWUSR , & bfad_debugfs_op_regrd , } ,
{ " regwr " , S_IFREG | S_IWUSR , & bfad_debugfs_op_regwr , } ,
} ;
static struct dentry * bfa_debugfs_root ;
static atomic_t bfa_debugfs_port_count ;
inline void
bfad_debugfs_init ( struct bfad_port_s * port )
{
2011-04-15 03:50:35 +04:00
struct bfad_s * bfad = port - > bfad ;
2010-07-09 07:02:55 +04:00
const struct bfad_debugfs_entry * file ;
2011-04-15 03:50:35 +04:00
char name [ 64 ] ;
2010-07-09 07:02:55 +04:00
int i ;
if ( ! bfa_debugfs_enable )
return ;
/* Setup the BFA debugfs root directory*/
if ( ! bfa_debugfs_root ) {
bfa_debugfs_root = debugfs_create_dir ( " bfa " , NULL ) ;
atomic_set ( & bfa_debugfs_port_count , 0 ) ;
if ( ! bfa_debugfs_root ) {
printk ( KERN_WARNING
" BFA debugfs root dir creation failed \n " ) ;
goto err ;
}
}
2011-04-15 03:50:35 +04:00
/* Setup the pci_dev debugfs directory for the port */
snprintf ( name , sizeof ( name ) , " pci_dev:%s " , bfad - > pci_name ) ;
2010-07-09 07:02:55 +04:00
if ( ! port - > port_debugfs_root ) {
port - > port_debugfs_root =
debugfs_create_dir ( name , bfa_debugfs_root ) ;
if ( ! port - > port_debugfs_root ) {
printk ( KERN_WARNING
2011-04-15 03:50:35 +04:00
" bfa %s: debugfs root creation failed \n " ,
bfad - > pci_name ) ;
2010-07-09 07:02:55 +04:00
goto err ;
}
atomic_inc ( & bfa_debugfs_port_count ) ;
for ( i = 0 ; i < ARRAY_SIZE ( bfad_debugfs_files ) ; i + + ) {
file = & bfad_debugfs_files [ i ] ;
bfad - > bfad_dentry_files [ i ] =
debugfs_create_file ( file - > name ,
file - > mode ,
port - > port_debugfs_root ,
port ,
file - > fops ) ;
if ( ! bfad - > bfad_dentry_files [ i ] ) {
printk ( KERN_WARNING
2011-04-15 03:50:35 +04:00
" bfa %s: debugfs %s creation failed \n " ,
bfad - > pci_name , file - > name ) ;
2010-07-09 07:02:55 +04:00
goto err ;
}
}
}
err :
return ;
}
inline void
bfad_debugfs_exit ( struct bfad_port_s * port )
{
2011-04-15 03:50:35 +04:00
struct bfad_s * bfad = port - > bfad ;
2010-07-09 07:02:55 +04:00
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( bfad_debugfs_files ) ; i + + ) {
if ( bfad - > bfad_dentry_files [ i ] ) {
debugfs_remove ( bfad - > bfad_dentry_files [ i ] ) ;
bfad - > bfad_dentry_files [ i ] = NULL ;
}
}
2011-11-17 00:28:49 +04:00
/* Remove the pci_dev debugfs directory for the port */
2010-07-09 07:02:55 +04:00
if ( port - > port_debugfs_root ) {
debugfs_remove ( port - > port_debugfs_root ) ;
port - > port_debugfs_root = NULL ;
atomic_dec ( & bfa_debugfs_port_count ) ;
}
/* Remove the BFA debugfs root directory */
if ( atomic_read ( & bfa_debugfs_port_count ) = = 0 ) {
debugfs_remove ( bfa_debugfs_root ) ;
bfa_debugfs_root = NULL ;
}
}