2019-05-30 02:57:23 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2015-08-21 11:29:38 +03:00
/*
* FUJITSU Extended Socket Network Device driver
* Copyright ( c ) 2015 FUJITSU LIMITED
*/
/* ethtool support for fjes */
# include <linux/vmalloc.h>
# include <linux/netdevice.h>
# include <linux/ethtool.h>
# include <linux/platform_device.h>
# include "fjes.h"
struct fjes_stats {
char stat_string [ ETH_GSTRING_LEN ] ;
int sizeof_stat ;
int stat_offset ;
} ;
# define FJES_STAT(name, stat) { \
. stat_string = name , \
2019-12-09 21:31:43 +03:00
. sizeof_stat = sizeof_field ( struct fjes_adapter , stat ) , \
2015-08-21 11:29:38 +03:00
. stat_offset = offsetof ( struct fjes_adapter , stat ) \
}
static const struct fjes_stats fjes_gstrings_stats [ ] = {
FJES_STAT ( " rx_packets " , stats64 . rx_packets ) ,
FJES_STAT ( " tx_packets " , stats64 . tx_packets ) ,
FJES_STAT ( " rx_bytes " , stats64 . rx_bytes ) ,
FJES_STAT ( " tx_bytes " , stats64 . rx_bytes ) ,
FJES_STAT ( " rx_dropped " , stats64 . rx_dropped ) ,
FJES_STAT ( " tx_dropped " , stats64 . tx_dropped ) ,
} ;
2016-10-14 14:27:32 +03:00
# define FJES_EP_STATS_LEN 14
# define FJES_STATS_LEN \
( ARRAY_SIZE ( fjes_gstrings_stats ) + \
( ( & ( ( struct fjes_adapter * ) netdev_priv ( netdev ) ) - > hw ) - > max_epid - 1 ) * \
FJES_EP_STATS_LEN )
2015-08-21 11:29:38 +03:00
static void fjes_get_ethtool_stats ( struct net_device * netdev ,
struct ethtool_stats * stats , u64 * data )
{
struct fjes_adapter * adapter = netdev_priv ( netdev ) ;
2016-10-14 14:27:32 +03:00
struct fjes_hw * hw = & adapter - > hw ;
int epidx ;
2015-08-21 11:29:38 +03:00
char * p ;
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( fjes_gstrings_stats ) ; i + + ) {
p = ( char * ) adapter + fjes_gstrings_stats [ i ] . stat_offset ;
data [ i ] = ( fjes_gstrings_stats [ i ] . sizeof_stat = = sizeof ( u64 ) )
? * ( u64 * ) p : * ( u32 * ) p ;
}
2016-10-14 14:27:32 +03:00
for ( epidx = 0 ; epidx < hw - > max_epid ; epidx + + ) {
if ( epidx = = hw - > my_epid )
continue ;
data [ i + + ] = hw - > ep_shm_info [ epidx ] . ep_stats
. com_regist_buf_exec ;
data [ i + + ] = hw - > ep_shm_info [ epidx ] . ep_stats
. com_unregist_buf_exec ;
data [ i + + ] = hw - > ep_shm_info [ epidx ] . ep_stats . send_intr_rx ;
data [ i + + ] = hw - > ep_shm_info [ epidx ] . ep_stats . send_intr_unshare ;
data [ i + + ] = hw - > ep_shm_info [ epidx ] . ep_stats
. send_intr_zoneupdate ;
data [ i + + ] = hw - > ep_shm_info [ epidx ] . ep_stats . recv_intr_rx ;
data [ i + + ] = hw - > ep_shm_info [ epidx ] . ep_stats . recv_intr_unshare ;
data [ i + + ] = hw - > ep_shm_info [ epidx ] . ep_stats . recv_intr_stop ;
data [ i + + ] = hw - > ep_shm_info [ epidx ] . ep_stats
. recv_intr_zoneupdate ;
data [ i + + ] = hw - > ep_shm_info [ epidx ] . ep_stats . tx_buffer_full ;
data [ i + + ] = hw - > ep_shm_info [ epidx ] . ep_stats
. tx_dropped_not_shared ;
data [ i + + ] = hw - > ep_shm_info [ epidx ] . ep_stats
. tx_dropped_ver_mismatch ;
data [ i + + ] = hw - > ep_shm_info [ epidx ] . ep_stats
. tx_dropped_buf_size_mismatch ;
data [ i + + ] = hw - > ep_shm_info [ epidx ] . ep_stats
. tx_dropped_vlanid_mismatch ;
}
2015-08-21 11:29:38 +03:00
}
static void fjes_get_strings ( struct net_device * netdev ,
u32 stringset , u8 * data )
{
2016-10-14 14:27:32 +03:00
struct fjes_adapter * adapter = netdev_priv ( netdev ) ;
struct fjes_hw * hw = & adapter - > hw ;
2015-08-21 11:29:38 +03:00
u8 * p = data ;
int i ;
switch ( stringset ) {
case ETH_SS_STATS :
for ( i = 0 ; i < ARRAY_SIZE ( fjes_gstrings_stats ) ; i + + ) {
memcpy ( p , fjes_gstrings_stats [ i ] . stat_string ,
ETH_GSTRING_LEN ) ;
p + = ETH_GSTRING_LEN ;
}
2016-10-14 14:27:32 +03:00
for ( i = 0 ; i < hw - > max_epid ; i + + ) {
if ( i = = hw - > my_epid )
continue ;
sprintf ( p , " ep%u_com_regist_buf_exec " , i ) ;
p + = ETH_GSTRING_LEN ;
sprintf ( p , " ep%u_com_unregist_buf_exec " , i ) ;
p + = ETH_GSTRING_LEN ;
sprintf ( p , " ep%u_send_intr_rx " , i ) ;
p + = ETH_GSTRING_LEN ;
sprintf ( p , " ep%u_send_intr_unshare " , i ) ;
p + = ETH_GSTRING_LEN ;
sprintf ( p , " ep%u_send_intr_zoneupdate " , i ) ;
p + = ETH_GSTRING_LEN ;
sprintf ( p , " ep%u_recv_intr_rx " , i ) ;
p + = ETH_GSTRING_LEN ;
sprintf ( p , " ep%u_recv_intr_unshare " , i ) ;
p + = ETH_GSTRING_LEN ;
sprintf ( p , " ep%u_recv_intr_stop " , i ) ;
p + = ETH_GSTRING_LEN ;
sprintf ( p , " ep%u_recv_intr_zoneupdate " , i ) ;
p + = ETH_GSTRING_LEN ;
sprintf ( p , " ep%u_tx_buffer_full " , i ) ;
p + = ETH_GSTRING_LEN ;
sprintf ( p , " ep%u_tx_dropped_not_shared " , i ) ;
p + = ETH_GSTRING_LEN ;
sprintf ( p , " ep%u_tx_dropped_ver_mismatch " , i ) ;
p + = ETH_GSTRING_LEN ;
sprintf ( p , " ep%u_tx_dropped_buf_size_mismatch " , i ) ;
p + = ETH_GSTRING_LEN ;
sprintf ( p , " ep%u_tx_dropped_vlanid_mismatch " , i ) ;
p + = ETH_GSTRING_LEN ;
}
2015-08-21 11:29:38 +03:00
break ;
}
}
static int fjes_get_sset_count ( struct net_device * netdev , int sset )
{
switch ( sset ) {
case ETH_SS_STATS :
2016-10-14 14:27:32 +03:00
return FJES_STATS_LEN ;
2015-08-21 11:29:38 +03:00
default :
return - EOPNOTSUPP ;
}
}
static void fjes_get_drvinfo ( struct net_device * netdev ,
struct ethtool_drvinfo * drvinfo )
{
struct fjes_adapter * adapter = netdev_priv ( netdev ) ;
struct platform_device * plat_dev ;
plat_dev = adapter - > plat_dev ;
strlcpy ( drvinfo - > driver , fjes_driver_name , sizeof ( drvinfo - > driver ) ) ;
strlcpy ( drvinfo - > version , fjes_driver_version ,
sizeof ( drvinfo - > version ) ) ;
strlcpy ( drvinfo - > fw_version , " none " , sizeof ( drvinfo - > fw_version ) ) ;
snprintf ( drvinfo - > bus_info , sizeof ( drvinfo - > bus_info ) ,
" platform:%s " , plat_dev - > name ) ;
}
2017-03-09 01:16:11 +03:00
static int fjes_get_link_ksettings ( struct net_device * netdev ,
struct ethtool_link_ksettings * ecmd )
2015-08-21 11:29:38 +03:00
{
2017-03-09 01:16:11 +03:00
ethtool_link_ksettings_zero_link_mode ( ecmd , supported ) ;
ethtool_link_ksettings_zero_link_mode ( ecmd , advertising ) ;
ecmd - > base . duplex = DUPLEX_FULL ;
ecmd - > base . autoneg = AUTONEG_DISABLE ;
ecmd - > base . port = PORT_NONE ;
ecmd - > base . speed = 20000 ; /* 20Gb/s */
2015-08-21 11:29:38 +03:00
return 0 ;
}
2016-10-14 14:27:25 +03:00
static int fjes_get_regs_len ( struct net_device * netdev )
{
# define FJES_REGS_LEN 37
return FJES_REGS_LEN * sizeof ( u32 ) ;
}
static void fjes_get_regs ( struct net_device * netdev ,
struct ethtool_regs * regs , void * p )
{
struct fjes_adapter * adapter = netdev_priv ( netdev ) ;
struct fjes_hw * hw = & adapter - > hw ;
u32 * regs_buff = p ;
memset ( p , 0 , FJES_REGS_LEN * sizeof ( u32 ) ) ;
regs - > version = 1 ;
/* Information registers */
regs_buff [ 0 ] = rd32 ( XSCT_OWNER_EPID ) ;
regs_buff [ 1 ] = rd32 ( XSCT_MAX_EP ) ;
/* Device Control registers */
regs_buff [ 4 ] = rd32 ( XSCT_DCTL ) ;
/* Command Control registers */
regs_buff [ 8 ] = rd32 ( XSCT_CR ) ;
regs_buff [ 9 ] = rd32 ( XSCT_CS ) ;
regs_buff [ 10 ] = rd32 ( XSCT_SHSTSAL ) ;
regs_buff [ 11 ] = rd32 ( XSCT_SHSTSAH ) ;
regs_buff [ 13 ] = rd32 ( XSCT_REQBL ) ;
regs_buff [ 14 ] = rd32 ( XSCT_REQBAL ) ;
regs_buff [ 15 ] = rd32 ( XSCT_REQBAH ) ;
regs_buff [ 17 ] = rd32 ( XSCT_RESPBL ) ;
regs_buff [ 18 ] = rd32 ( XSCT_RESPBAL ) ;
regs_buff [ 19 ] = rd32 ( XSCT_RESPBAH ) ;
/* Interrupt Control registers */
regs_buff [ 32 ] = rd32 ( XSCT_IS ) ;
regs_buff [ 33 ] = rd32 ( XSCT_IMS ) ;
regs_buff [ 34 ] = rd32 ( XSCT_IMC ) ;
regs_buff [ 35 ] = rd32 ( XSCT_IG ) ;
regs_buff [ 36 ] = rd32 ( XSCT_ICTL ) ;
}
2016-10-14 14:27:45 +03:00
static int fjes_set_dump ( struct net_device * netdev , struct ethtool_dump * dump )
{
struct fjes_adapter * adapter = netdev_priv ( netdev ) ;
struct fjes_hw * hw = & adapter - > hw ;
int ret = 0 ;
if ( dump - > flag ) {
if ( hw - > debug_mode )
return - EPERM ;
hw - > debug_mode = dump - > flag ;
/* enable debug mode */
mutex_lock ( & hw - > hw_info . lock ) ;
ret = fjes_hw_start_debug ( hw ) ;
mutex_unlock ( & hw - > hw_info . lock ) ;
if ( ret )
hw - > debug_mode = 0 ;
} else {
if ( ! hw - > debug_mode )
return - EPERM ;
/* disable debug mode */
mutex_lock ( & hw - > hw_info . lock ) ;
ret = fjes_hw_stop_debug ( hw ) ;
mutex_unlock ( & hw - > hw_info . lock ) ;
}
return ret ;
}
static int fjes_get_dump_flag ( struct net_device * netdev ,
struct ethtool_dump * dump )
{
struct fjes_adapter * adapter = netdev_priv ( netdev ) ;
struct fjes_hw * hw = & adapter - > hw ;
dump - > len = hw - > hw_info . trace_size ;
dump - > version = 1 ;
dump - > flag = hw - > debug_mode ;
return 0 ;
}
static int fjes_get_dump_data ( struct net_device * netdev ,
struct ethtool_dump * dump , void * buf )
{
struct fjes_adapter * adapter = netdev_priv ( netdev ) ;
struct fjes_hw * hw = & adapter - > hw ;
int ret = 0 ;
if ( hw - > hw_info . trace )
memcpy ( buf , hw - > hw_info . trace , hw - > hw_info . trace_size ) ;
else
ret = - EPERM ;
return ret ;
}
2015-08-21 11:29:38 +03:00
static const struct ethtool_ops fjes_ethtool_ops = {
. get_drvinfo = fjes_get_drvinfo ,
. get_ethtool_stats = fjes_get_ethtool_stats ,
. get_strings = fjes_get_strings ,
. get_sset_count = fjes_get_sset_count ,
2016-10-14 14:27:25 +03:00
. get_regs = fjes_get_regs ,
. get_regs_len = fjes_get_regs_len ,
2016-10-14 14:27:45 +03:00
. set_dump = fjes_set_dump ,
. get_dump_flag = fjes_get_dump_flag ,
. get_dump_data = fjes_get_dump_data ,
2017-03-09 01:16:11 +03:00
. get_link_ksettings = fjes_get_link_ksettings ,
2015-08-21 11:29:38 +03:00
} ;
void fjes_set_ethtool_ops ( struct net_device * netdev )
{
netdev - > ethtool_ops = & fjes_ethtool_ops ;
}