2008-04-27 12:55:59 +01:00
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
* Copyright 2005 - 2006 Fen Systems Ltd .
2011-02-25 00:01:34 +00:00
* Copyright 2006 - 2010 Solarflare Communications Inc .
2008-04-27 12:55:59 +01:00
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation , incorporated herein by reference .
*/
# include <linux/netdevice.h>
# include <linux/ethtool.h>
# include <linux/rtnetlink.h>
2010-12-07 19:11:26 +00:00
# include <linux/in.h>
2008-04-27 12:55:59 +01:00
# include "net_driver.h"
2008-12-12 21:50:46 -08:00
# include "workarounds.h"
2008-05-07 13:36:19 +01:00
# include "selftest.h"
2008-04-27 12:55:59 +01:00
# include "efx.h"
2010-09-20 08:43:42 +00:00
# include "filter.h"
2009-11-29 15:12:08 +00:00
# include "nic.h"
2008-04-27 12:55:59 +01:00
struct ethtool_string {
char name [ ETH_GSTRING_LEN ] ;
} ;
struct efx_ethtool_stat {
const char * name ;
enum {
EFX_ETHTOOL_STAT_SOURCE_mac_stats ,
EFX_ETHTOOL_STAT_SOURCE_nic ,
2011-02-18 19:14:13 +00:00
EFX_ETHTOOL_STAT_SOURCE_channel ,
EFX_ETHTOOL_STAT_SOURCE_tx_queue
2008-04-27 12:55:59 +01:00
} source ;
unsigned offset ;
u64 ( * get_stat ) ( void * field ) ; /* Reader function */
} ;
/* Initialiser for a struct #efx_ethtool_stat with type-checking */
# define EFX_ETHTOOL_STAT(stat_name, source_name, field, field_type, \
get_stat_function ) { \
. name = # stat_name , \
. source = EFX_ETHTOOL_STAT_SOURCE_ # # source_name , \
. offset = ( ( ( ( field_type * ) 0 ) = = \
& ( ( struct efx_ # # source_name * ) 0 ) - > field ) ? \
offsetof ( struct efx_ # # source_name , field ) : \
offsetof ( struct efx_ # # source_name , field ) ) , \
. get_stat = get_stat_function , \
}
static u64 efx_get_uint_stat ( void * field )
{
return * ( unsigned int * ) field ;
}
static u64 efx_get_u64_stat ( void * field )
{
return * ( u64 * ) field ;
}
static u64 efx_get_atomic_stat ( void * field )
{
return atomic_read ( ( atomic_t * ) field ) ;
}
# define EFX_ETHTOOL_U64_MAC_STAT(field) \
2012-01-05 17:19:45 +00:00
EFX_ETHTOOL_STAT ( field , mac_stats , field , \
2008-04-27 12:55:59 +01:00
u64 , efx_get_u64_stat )
# define EFX_ETHTOOL_UINT_NIC_STAT(name) \
EFX_ETHTOOL_STAT ( name , nic , n_ # # name , \
unsigned int , efx_get_uint_stat )
# define EFX_ETHTOOL_ATOMIC_NIC_ERROR_STAT(field) \
EFX_ETHTOOL_STAT ( field , nic , field , \
atomic_t , efx_get_atomic_stat )
# define EFX_ETHTOOL_UINT_CHANNEL_STAT(field) \
EFX_ETHTOOL_STAT ( field , channel , n_ # # field , \
unsigned int , efx_get_uint_stat )
2011-02-18 19:14:13 +00:00
# define EFX_ETHTOOL_UINT_TXQ_STAT(field) \
EFX_ETHTOOL_STAT ( tx_ # # field , tx_queue , field , \
unsigned int , efx_get_uint_stat )
2012-01-05 19:05:20 +00:00
static const struct efx_ethtool_stat efx_ethtool_stats [ ] = {
2008-04-27 12:55:59 +01:00
EFX_ETHTOOL_U64_MAC_STAT ( tx_bytes ) ,
EFX_ETHTOOL_U64_MAC_STAT ( tx_good_bytes ) ,
EFX_ETHTOOL_U64_MAC_STAT ( tx_bad_bytes ) ,
2011-10-12 17:20:25 +01:00
EFX_ETHTOOL_U64_MAC_STAT ( tx_packets ) ,
EFX_ETHTOOL_U64_MAC_STAT ( tx_bad ) ,
EFX_ETHTOOL_U64_MAC_STAT ( tx_pause ) ,
EFX_ETHTOOL_U64_MAC_STAT ( tx_control ) ,
EFX_ETHTOOL_U64_MAC_STAT ( tx_unicast ) ,
EFX_ETHTOOL_U64_MAC_STAT ( tx_multicast ) ,
EFX_ETHTOOL_U64_MAC_STAT ( tx_broadcast ) ,
EFX_ETHTOOL_U64_MAC_STAT ( tx_lt64 ) ,
EFX_ETHTOOL_U64_MAC_STAT ( tx_64 ) ,
EFX_ETHTOOL_U64_MAC_STAT ( tx_65_to_127 ) ,
EFX_ETHTOOL_U64_MAC_STAT ( tx_128_to_255 ) ,
EFX_ETHTOOL_U64_MAC_STAT ( tx_256_to_511 ) ,
EFX_ETHTOOL_U64_MAC_STAT ( tx_512_to_1023 ) ,
EFX_ETHTOOL_U64_MAC_STAT ( tx_1024_to_15xx ) ,
EFX_ETHTOOL_U64_MAC_STAT ( tx_15xx_to_jumbo ) ,
EFX_ETHTOOL_U64_MAC_STAT ( tx_gtjumbo ) ,
EFX_ETHTOOL_U64_MAC_STAT ( tx_collision ) ,
EFX_ETHTOOL_U64_MAC_STAT ( tx_single_collision ) ,
EFX_ETHTOOL_U64_MAC_STAT ( tx_multiple_collision ) ,
EFX_ETHTOOL_U64_MAC_STAT ( tx_excessive_collision ) ,
EFX_ETHTOOL_U64_MAC_STAT ( tx_deferred ) ,
EFX_ETHTOOL_U64_MAC_STAT ( tx_late_collision ) ,
EFX_ETHTOOL_U64_MAC_STAT ( tx_excessive_deferred ) ,
EFX_ETHTOOL_U64_MAC_STAT ( tx_non_tcpudp ) ,
EFX_ETHTOOL_U64_MAC_STAT ( tx_mac_src_error ) ,
EFX_ETHTOOL_U64_MAC_STAT ( tx_ip_src_error ) ,
2011-02-18 19:14:13 +00:00
EFX_ETHTOOL_UINT_TXQ_STAT ( tso_bursts ) ,
EFX_ETHTOOL_UINT_TXQ_STAT ( tso_long_headers ) ,
EFX_ETHTOOL_UINT_TXQ_STAT ( tso_packets ) ,
EFX_ETHTOOL_UINT_TXQ_STAT ( pushes ) ,
2008-04-27 12:55:59 +01:00
EFX_ETHTOOL_U64_MAC_STAT ( rx_bytes ) ,
EFX_ETHTOOL_U64_MAC_STAT ( rx_good_bytes ) ,
EFX_ETHTOOL_U64_MAC_STAT ( rx_bad_bytes ) ,
2011-10-12 17:20:25 +01:00
EFX_ETHTOOL_U64_MAC_STAT ( rx_packets ) ,
EFX_ETHTOOL_U64_MAC_STAT ( rx_good ) ,
EFX_ETHTOOL_U64_MAC_STAT ( rx_bad ) ,
EFX_ETHTOOL_U64_MAC_STAT ( rx_pause ) ,
EFX_ETHTOOL_U64_MAC_STAT ( rx_control ) ,
EFX_ETHTOOL_U64_MAC_STAT ( rx_unicast ) ,
EFX_ETHTOOL_U64_MAC_STAT ( rx_multicast ) ,
EFX_ETHTOOL_U64_MAC_STAT ( rx_broadcast ) ,
EFX_ETHTOOL_U64_MAC_STAT ( rx_lt64 ) ,
EFX_ETHTOOL_U64_MAC_STAT ( rx_64 ) ,
EFX_ETHTOOL_U64_MAC_STAT ( rx_65_to_127 ) ,
EFX_ETHTOOL_U64_MAC_STAT ( rx_128_to_255 ) ,
EFX_ETHTOOL_U64_MAC_STAT ( rx_256_to_511 ) ,
EFX_ETHTOOL_U64_MAC_STAT ( rx_512_to_1023 ) ,
EFX_ETHTOOL_U64_MAC_STAT ( rx_1024_to_15xx ) ,
EFX_ETHTOOL_U64_MAC_STAT ( rx_15xx_to_jumbo ) ,
EFX_ETHTOOL_U64_MAC_STAT ( rx_gtjumbo ) ,
EFX_ETHTOOL_U64_MAC_STAT ( rx_bad_lt64 ) ,
EFX_ETHTOOL_U64_MAC_STAT ( rx_bad_64_to_15xx ) ,
EFX_ETHTOOL_U64_MAC_STAT ( rx_bad_15xx_to_jumbo ) ,
EFX_ETHTOOL_U64_MAC_STAT ( rx_bad_gtjumbo ) ,
EFX_ETHTOOL_U64_MAC_STAT ( rx_overflow ) ,
EFX_ETHTOOL_U64_MAC_STAT ( rx_missed ) ,
EFX_ETHTOOL_U64_MAC_STAT ( rx_false_carrier ) ,
EFX_ETHTOOL_U64_MAC_STAT ( rx_symbol_error ) ,
EFX_ETHTOOL_U64_MAC_STAT ( rx_align_error ) ,
EFX_ETHTOOL_U64_MAC_STAT ( rx_length_error ) ,
EFX_ETHTOOL_U64_MAC_STAT ( rx_internal_error ) ,
2008-04-27 12:55:59 +01:00
EFX_ETHTOOL_UINT_NIC_STAT ( rx_nodesc_drop_cnt ) ,
EFX_ETHTOOL_ATOMIC_NIC_ERROR_STAT ( rx_reset ) ,
EFX_ETHTOOL_UINT_CHANNEL_STAT ( rx_tobe_disc ) ,
EFX_ETHTOOL_UINT_CHANNEL_STAT ( rx_ip_hdr_chksum_err ) ,
EFX_ETHTOOL_UINT_CHANNEL_STAT ( rx_tcp_udp_chksum_err ) ,
2009-11-28 05:36:29 +00:00
EFX_ETHTOOL_UINT_CHANNEL_STAT ( rx_mcast_mismatch ) ,
2008-04-27 12:55:59 +01:00
EFX_ETHTOOL_UINT_CHANNEL_STAT ( rx_frm_trunc ) ,
} ;
/* Number of ethtool statistics */
# define EFX_ETHTOOL_NUM_STATS ARRAY_SIZE(efx_ethtool_stats)
2008-09-01 12:47:16 +01:00
# define EFX_ETHTOOL_EEPROM_MAGIC 0xEFAB
2008-04-27 12:55:59 +01:00
/**************************************************************************
*
* Ethtool operations
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
/* Identify device by flashing LEDs */
2011-04-02 00:43:46 +01:00
static int efx_ethtool_phys_id ( struct net_device * net_dev ,
enum ethtool_phys_id_state state )
2008-04-27 12:55:59 +01:00
{
2008-09-01 12:43:14 +01:00
struct efx_nic * efx = netdev_priv ( net_dev ) ;
2011-04-13 13:09:10 +00:00
enum efx_led_mode mode = EFX_LED_DEFAULT ;
2008-04-27 12:55:59 +01:00
2011-04-02 00:43:46 +01:00
switch ( state ) {
case ETHTOOL_ID_ON :
mode = EFX_LED_ON ;
break ;
case ETHTOOL_ID_OFF :
mode = EFX_LED_OFF ;
break ;
case ETHTOOL_ID_INACTIVE :
mode = EFX_LED_DEFAULT ;
break ;
2011-04-13 13:09:10 +00:00
case ETHTOOL_ID_ACTIVE :
return 1 ; /* cycle on/off once per second */
2011-04-02 00:43:46 +01:00
}
2009-11-23 16:03:45 +00:00
2011-04-02 00:43:46 +01:00
efx - > type - > set_id_led ( efx , mode ) ;
2008-04-27 12:55:59 +01:00
return 0 ;
}
/* This must be called with rtnl_lock held. */
2010-10-18 05:27:31 +00:00
static int efx_ethtool_get_settings ( struct net_device * net_dev ,
struct ethtool_cmd * ecmd )
2008-04-27 12:55:59 +01:00
{
2008-09-01 12:43:14 +01:00
struct efx_nic * efx = netdev_priv ( net_dev ) ;
2009-11-29 03:42:41 +00:00
struct efx_link_state * link_state = & efx - > link_state ;
2008-04-27 12:55:59 +01:00
mutex_lock ( & efx - > mac_lock ) ;
2008-12-12 21:50:08 -08:00
efx - > phy_op - > get_settings ( efx , ecmd ) ;
2008-04-27 12:55:59 +01:00
mutex_unlock ( & efx - > mac_lock ) ;
2010-02-03 09:31:57 +00:00
/* GMAC does not support 1000Mbps HD */
2008-12-12 21:50:08 -08:00
ecmd - > supported & = ~ SUPPORTED_1000baseT_Half ;
2009-11-29 03:42:41 +00:00
/* Both MACs support pause frames (bidirectional and respond-only) */
ecmd - > supported | = SUPPORTED_Pause | SUPPORTED_Asym_Pause ;
if ( LOOPBACK_INTERNAL ( efx ) ) {
2011-04-27 18:32:40 +00:00
ethtool_cmd_speed_set ( ecmd , link_state - > speed ) ;
2009-11-29 03:42:41 +00:00
ecmd - > duplex = link_state - > fd ? DUPLEX_FULL : DUPLEX_HALF ;
}
2008-12-12 21:50:08 -08:00
return 0 ;
2008-04-27 12:55:59 +01:00
}
/* This must be called with rtnl_lock held. */
2010-10-18 05:27:31 +00:00
static int efx_ethtool_set_settings ( struct net_device * net_dev ,
struct ethtool_cmd * ecmd )
2008-04-27 12:55:59 +01:00
{
2008-09-01 12:43:14 +01:00
struct efx_nic * efx = netdev_priv ( net_dev ) ;
2008-04-27 12:55:59 +01:00
int rc ;
2010-02-03 09:31:57 +00:00
/* GMAC does not support 1000Mbps HD */
2011-04-27 18:32:39 +00:00
if ( ( ethtool_cmd_speed ( ecmd ) = = SPEED_1000 ) & &
( ecmd - > duplex ! = DUPLEX_FULL ) ) {
2010-06-23 11:30:07 +00:00
netif_dbg ( efx , drv , efx - > net_dev ,
" rejecting unsupported 1000Mbps HD setting \n " ) ;
2008-12-12 21:50:08 -08:00
return - EINVAL ;
}
2008-04-27 12:55:59 +01:00
mutex_lock ( & efx - > mac_lock ) ;
2008-12-12 21:50:08 -08:00
rc = efx - > phy_op - > set_settings ( efx , ecmd ) ;
2008-04-27 12:55:59 +01:00
mutex_unlock ( & efx - > mac_lock ) ;
return rc ;
}
static void efx_ethtool_get_drvinfo ( struct net_device * net_dev ,
struct ethtool_drvinfo * info )
{
2008-09-01 12:43:14 +01:00
struct efx_nic * efx = netdev_priv ( net_dev ) ;
2008-04-27 12:55:59 +01:00
2010-06-23 11:30:26 +00:00
strlcpy ( info - > driver , KBUILD_MODNAME , sizeof ( info - > driver ) ) ;
2008-04-27 12:55:59 +01:00
strlcpy ( info - > version , EFX_DRIVER_VERSION , sizeof ( info - > version ) ) ;
2009-11-29 15:15:41 +00:00
if ( efx_nic_rev ( efx ) > = EFX_REV_SIENA_A0 )
2011-02-24 23:57:47 +00:00
efx_mcdi_print_fwver ( efx , info - > fw_version ,
sizeof ( info - > fw_version ) ) ;
2008-04-27 12:55:59 +01:00
strlcpy ( info - > bus_info , pci_name ( efx - > pci_dev ) , sizeof ( info - > bus_info ) ) ;
}
2010-06-21 03:06:53 +00:00
static int efx_ethtool_get_regs_len ( struct net_device * net_dev )
{
return efx_nic_get_regs_len ( netdev_priv ( net_dev ) ) ;
}
static void efx_ethtool_get_regs ( struct net_device * net_dev ,
struct ethtool_regs * regs , void * buf )
{
struct efx_nic * efx = netdev_priv ( net_dev ) ;
regs - > version = efx - > type - > revision ;
efx_nic_get_regs ( efx , buf ) ;
}
2010-06-23 11:30:07 +00:00
static u32 efx_ethtool_get_msglevel ( struct net_device * net_dev )
{
struct efx_nic * efx = netdev_priv ( net_dev ) ;
return efx - > msg_enable ;
}
static void efx_ethtool_set_msglevel ( struct net_device * net_dev , u32 msg_enable )
{
struct efx_nic * efx = netdev_priv ( net_dev ) ;
efx - > msg_enable = msg_enable ;
}
2008-05-07 13:36:19 +01:00
/**
* efx_fill_test - fill in an individual self - test entry
* @ test_index : Index of the test
* @ strings : Ethtool strings , or % NULL
* @ data : Ethtool test results , or % NULL
* @ test : Pointer to test result ( used only if data ! = % NULL )
2008-12-12 21:41:55 -08:00
* @ unit_format : Unit name format ( e . g . " chan \ %d " )
* @ unit_id : Unit id ( e . g . 0 for " chan0 " )
2008-05-07 13:36:19 +01:00
* @ test_format : Test name format ( e . g . " loopback. \ %s.tx.sent " )
2008-12-12 21:41:55 -08:00
* @ test_id : Test id ( e . g . " PHYXS " for " loopback.PHYXS.tx_sent " )
2008-05-07 13:36:19 +01:00
*
* Fill in an individual self - test entry .
*/
static void efx_fill_test ( unsigned int test_index ,
struct ethtool_string * strings , u64 * data ,
int * test , const char * unit_format , int unit_id ,
const char * test_format , const char * test_id )
{
struct ethtool_string unit_str , test_str ;
/* Fill data value, if applicable */
if ( data )
data [ test_index ] = * test ;
/* Fill string, if applicable */
if ( strings ) {
2008-12-12 22:05:48 -08:00
if ( strchr ( unit_format , ' % ' ) )
snprintf ( unit_str . name , sizeof ( unit_str . name ) ,
unit_format , unit_id ) ;
else
strcpy ( unit_str . name , unit_format ) ;
2008-05-07 13:36:19 +01:00
snprintf ( test_str . name , sizeof ( test_str . name ) ,
test_format , test_id ) ;
snprintf ( strings [ test_index ] . name ,
sizeof ( strings [ test_index ] . name ) ,
2008-12-12 21:41:55 -08:00
" %-6s %-24s " , unit_str . name , test_str . name ) ;
2008-05-07 13:36:19 +01:00
}
}
2008-12-12 21:41:55 -08:00
# define EFX_CHANNEL_NAME(_channel) "chan%d", _channel->channel
2008-05-07 13:36:19 +01:00
# define EFX_TX_QUEUE_NAME(_tx_queue) "txq%d", _tx_queue->queue
# define EFX_RX_QUEUE_NAME(_rx_queue) "rxq%d", _rx_queue->queue
# define EFX_LOOPBACK_NAME(_mode, _counter) \
2009-11-23 16:08:17 +00:00
" loopback.%s. " _counter , STRING_TABLE_LOOKUP ( _mode , efx_loopback_mode )
2008-05-07 13:36:19 +01:00
/**
* efx_fill_loopback_test - fill in a block of loopback self - test entries
* @ efx : Efx NIC
* @ lb_tests : Efx loopback self - test results structure
* @ mode : Loopback test mode
* @ test_index : Starting index of the test
* @ strings : Ethtool strings , or % NULL
* @ data : Ethtool test results , or % NULL
*/
static int efx_fill_loopback_test ( struct efx_nic * efx ,
struct efx_loopback_self_tests * lb_tests ,
enum efx_loopback_mode mode ,
unsigned int test_index ,
struct ethtool_string * strings , u64 * data )
{
2010-09-10 06:41:47 +00:00
struct efx_channel * channel = efx_get_channel ( efx , 0 ) ;
2008-05-07 13:36:19 +01:00
struct efx_tx_queue * tx_queue ;
2010-09-10 06:41:47 +00:00
efx_for_each_channel_tx_queue ( tx_queue , channel ) {
2008-05-07 13:36:19 +01:00
efx_fill_test ( test_index + + , strings , data ,
& lb_tests - > tx_sent [ tx_queue - > queue ] ,
EFX_TX_QUEUE_NAME ( tx_queue ) ,
EFX_LOOPBACK_NAME ( mode , " tx_sent " ) ) ;
efx_fill_test ( test_index + + , strings , data ,
& lb_tests - > tx_done [ tx_queue - > queue ] ,
EFX_TX_QUEUE_NAME ( tx_queue ) ,
EFX_LOOPBACK_NAME ( mode , " tx_done " ) ) ;
}
efx_fill_test ( test_index + + , strings , data ,
& lb_tests - > rx_good ,
2008-12-12 22:05:48 -08:00
" rx " , 0 ,
2008-05-07 13:36:19 +01:00
EFX_LOOPBACK_NAME ( mode , " rx_good " ) ) ;
efx_fill_test ( test_index + + , strings , data ,
& lb_tests - > rx_bad ,
2008-12-12 22:05:48 -08:00
" rx " , 0 ,
2008-05-07 13:36:19 +01:00
EFX_LOOPBACK_NAME ( mode , " rx_bad " ) ) ;
return test_index ;
}
/**
* efx_ethtool_fill_self_tests - get self - test details
* @ efx : Efx NIC
* @ tests : Efx self - test results structure , or % NULL
* @ strings : Ethtool strings , or % NULL
* @ data : Ethtool test results , or % NULL
*/
static int efx_ethtool_fill_self_tests ( struct efx_nic * efx ,
struct efx_self_tests * tests ,
struct ethtool_string * strings ,
u64 * data )
{
struct efx_channel * channel ;
2008-12-26 13:47:25 -08:00
unsigned int n = 0 , i ;
2008-05-07 13:36:19 +01:00
enum efx_loopback_mode mode ;
2010-02-03 09:30:50 +00:00
efx_fill_test ( n + + , strings , data , & tests - > phy_alive ,
" phy " , 0 , " alive " , NULL ) ;
2008-09-01 12:49:02 +01:00
efx_fill_test ( n + + , strings , data , & tests - > nvram ,
" core " , 0 , " nvram " , NULL ) ;
2008-05-07 13:36:19 +01:00
efx_fill_test ( n + + , strings , data , & tests - > interrupt ,
" core " , 0 , " interrupt " , NULL ) ;
/* Event queues */
efx_for_each_channel ( channel , efx ) {
efx_fill_test ( n + + , strings , data ,
& tests - > eventq_dma [ channel - > channel ] ,
EFX_CHANNEL_NAME ( channel ) ,
" eventq.dma " , NULL ) ;
efx_fill_test ( n + + , strings , data ,
& tests - > eventq_int [ channel - > channel ] ,
EFX_CHANNEL_NAME ( channel ) ,
" eventq.int " , NULL ) ;
}
2008-09-01 12:49:02 +01:00
efx_fill_test ( n + + , strings , data , & tests - > registers ,
" core " , 0 , " registers " , NULL ) ;
2008-12-26 13:47:25 -08:00
2009-11-29 15:08:55 +00:00
if ( efx - > phy_op - > run_tests ! = NULL ) {
EFX_BUG_ON_PARANOID ( efx - > phy_op - > test_name = = NULL ) ;
for ( i = 0 ; true ; + + i ) {
const char * name ;
EFX_BUG_ON_PARANOID ( i > = EFX_MAX_PHY_TESTS ) ;
name = efx - > phy_op - > test_name ( efx , i ) ;
if ( name = = NULL )
break ;
2010-02-03 09:30:50 +00:00
efx_fill_test ( n + + , strings , data , & tests - > phy_ext [ i ] ,
2009-11-29 15:08:55 +00:00
" phy " , 0 , name , NULL ) ;
}
}
2008-05-07 13:36:19 +01:00
/* Loopback tests */
2008-09-01 12:49:02 +01:00
for ( mode = LOOPBACK_NONE ; mode < = LOOPBACK_TEST_MAX ; mode + + ) {
2008-05-07 13:36:19 +01:00
if ( ! ( efx - > loopback_modes & ( 1 < < mode ) ) )
continue ;
n = efx_fill_loopback_test ( efx ,
& tests - > loopback [ mode ] , mode , n ,
strings , data ) ;
}
return n ;
}
2008-09-01 12:48:12 +01:00
static int efx_ethtool_get_sset_count ( struct net_device * net_dev ,
int string_set )
2008-04-27 12:55:59 +01:00
{
2008-09-01 12:48:12 +01:00
switch ( string_set ) {
case ETH_SS_STATS :
return EFX_ETHTOOL_NUM_STATS ;
case ETH_SS_TEST :
return efx_ethtool_fill_self_tests ( netdev_priv ( net_dev ) ,
NULL , NULL , NULL ) ;
default :
return - EINVAL ;
}
2008-05-07 13:36:19 +01:00
}
2008-04-27 12:55:59 +01:00
static void efx_ethtool_get_strings ( struct net_device * net_dev ,
u32 string_set , u8 * strings )
{
2008-09-01 12:43:14 +01:00
struct efx_nic * efx = netdev_priv ( net_dev ) ;
2008-04-27 12:55:59 +01:00
struct ethtool_string * ethtool_strings =
( struct ethtool_string * ) strings ;
int i ;
2008-05-07 13:36:19 +01:00
switch ( string_set ) {
case ETH_SS_STATS :
2008-04-27 12:55:59 +01:00
for ( i = 0 ; i < EFX_ETHTOOL_NUM_STATS ; i + + )
strncpy ( ethtool_strings [ i ] . name ,
efx_ethtool_stats [ i ] . name ,
sizeof ( ethtool_strings [ i ] . name ) ) ;
2008-05-07 13:36:19 +01:00
break ;
case ETH_SS_TEST :
efx_ethtool_fill_self_tests ( efx , NULL ,
ethtool_strings , NULL ) ;
break ;
default :
/* No other string sets */
break ;
}
2008-04-27 12:55:59 +01:00
}
static void efx_ethtool_get_stats ( struct net_device * net_dev ,
struct ethtool_stats * stats ,
u64 * data )
{
2008-09-01 12:43:14 +01:00
struct efx_nic * efx = netdev_priv ( net_dev ) ;
2008-04-27 12:55:59 +01:00
struct efx_mac_stats * mac_stats = & efx - > mac_stats ;
2012-01-05 19:05:20 +00:00
const struct efx_ethtool_stat * stat ;
2008-04-27 12:55:59 +01:00
struct efx_channel * channel ;
2011-02-18 19:14:13 +00:00
struct efx_tx_queue * tx_queue ;
2008-04-27 12:55:59 +01:00
int i ;
EFX_BUG_ON_PARANOID ( stats - > n_stats ! = EFX_ETHTOOL_NUM_STATS ) ;
2011-09-02 23:23:00 +01:00
spin_lock_bh ( & efx - > stats_lock ) ;
2008-04-27 12:55:59 +01:00
/* Update MAC and NIC statistics */
2011-09-02 23:23:00 +01:00
efx - > type - > update_stats ( efx ) ;
2008-04-27 12:55:59 +01:00
/* Fill detailed statistics buffer */
for ( i = 0 ; i < EFX_ETHTOOL_NUM_STATS ; i + + ) {
stat = & efx_ethtool_stats [ i ] ;
switch ( stat - > source ) {
case EFX_ETHTOOL_STAT_SOURCE_mac_stats :
data [ i ] = stat - > get_stat ( ( void * ) mac_stats +
stat - > offset ) ;
break ;
case EFX_ETHTOOL_STAT_SOURCE_nic :
data [ i ] = stat - > get_stat ( ( void * ) efx + stat - > offset ) ;
break ;
case EFX_ETHTOOL_STAT_SOURCE_channel :
data [ i ] = 0 ;
efx_for_each_channel ( channel , efx )
data [ i ] + = stat - > get_stat ( ( void * ) channel +
stat - > offset ) ;
break ;
2011-02-18 19:14:13 +00:00
case EFX_ETHTOOL_STAT_SOURCE_tx_queue :
data [ i ] = 0 ;
efx_for_each_channel ( channel , efx ) {
efx_for_each_channel_tx_queue ( tx_queue , channel )
data [ i ] + =
stat - > get_stat ( ( void * ) tx_queue
+ stat - > offset ) ;
}
break ;
2008-04-27 12:55:59 +01:00
}
}
2011-09-02 23:23:00 +01:00
spin_unlock_bh ( & efx - > stats_lock ) ;
2008-04-27 12:55:59 +01:00
}
2008-05-07 13:36:19 +01:00
static void efx_ethtool_self_test ( struct net_device * net_dev ,
struct ethtool_test * test , u64 * data )
{
2008-09-01 12:43:14 +01:00
struct efx_nic * efx = netdev_priv ( net_dev ) ;
2011-02-16 03:48:38 +00:00
struct efx_self_tests * efx_tests ;
2008-12-26 13:47:04 -08:00
int already_up ;
2011-02-16 03:48:38 +00:00
int rc = - ENOMEM ;
efx_tests = kzalloc ( sizeof ( * efx_tests ) , GFP_KERNEL ) ;
if ( ! efx_tests )
goto fail ;
2008-05-07 13:36:19 +01:00
ASSERT_RTNL ( ) ;
if ( efx - > state ! = STATE_RUNNING ) {
rc = - EIO ;
goto fail1 ;
}
2010-12-07 18:29:52 +00:00
netif_info ( efx , drv , efx - > net_dev , " starting %sline testing \n " ,
( test - > flags & ETH_TEST_FL_OFFLINE ) ? " off " : " on " ) ;
2008-05-07 13:36:19 +01:00
/* We need rx buffers and interrupts. */
already_up = ( efx - > net_dev - > flags & IFF_UP ) ;
if ( ! already_up ) {
rc = dev_open ( efx - > net_dev ) ;
if ( rc ) {
2010-06-23 11:30:07 +00:00
netif_err ( efx , drv , efx - > net_dev ,
" failed opening device. \n " ) ;
2011-02-16 03:48:38 +00:00
goto fail1 ;
2008-05-07 13:36:19 +01:00
}
}
2011-02-16 03:48:38 +00:00
rc = efx_selftest ( efx , efx_tests , test - > flags ) ;
2008-05-07 13:36:19 +01:00
if ( ! already_up )
dev_close ( efx - > net_dev ) ;
2010-12-07 18:29:52 +00:00
netif_info ( efx , drv , efx - > net_dev , " %s %sline self-tests \n " ,
rc = = 0 ? " passed " : " failed " ,
( test - > flags & ETH_TEST_FL_OFFLINE ) ? " off " : " on " ) ;
2008-05-07 13:36:19 +01:00
2011-02-16 03:48:38 +00:00
fail1 :
2008-05-07 13:36:19 +01:00
/* Fill ethtool results structures */
2011-02-16 03:48:38 +00:00
efx_ethtool_fill_self_tests ( efx , efx_tests , NULL , data ) ;
kfree ( efx_tests ) ;
fail :
2008-05-07 13:36:19 +01:00
if ( rc )
test - > flags | = ETH_TEST_FL_FAILED ;
}
2008-04-27 12:55:59 +01:00
/* Restart autonegotiation */
static int efx_ethtool_nway_reset ( struct net_device * net_dev )
{
2008-09-01 12:43:14 +01:00
struct efx_nic * efx = netdev_priv ( net_dev ) ;
2008-04-27 12:55:59 +01:00
2009-04-29 08:05:08 +00:00
return mdio45_nway_restart ( & efx - > mdio ) ;
2008-04-27 12:55:59 +01:00
}
2011-09-05 07:42:25 +00:00
/*
* Each channel has a single IRQ and moderation timer , started by any
* completion ( or other event ) . Unless the module parameter
* separate_tx_channels is set , IRQs and moderation are therefore
* shared between RX and TX completions . In this case , when RX IRQ
* moderation is explicitly changed then TX IRQ moderation is
* automatically changed too , but otherwise we fail if the two values
* are requested to be different .
*
2011-09-05 07:43:49 +00:00
* The hardware does not support a limit on the number of completions
* before an IRQ , so we do not use the max_frames fields . We should
* report and require that max_frames = = ( usecs ! = 0 ) , but this would
* invalidate existing user documentation .
*
* The hardware does not have distinct settings for interrupt
* moderation while the previous IRQ is being handled , so we should
* not use the ' irq ' fields . However , an earlier developer
* misunderstood the meaning of the ' irq ' fields and the driver did
* not support the standard fields . To avoid invalidating existing
* user documentation , we report and accept changes through either the
* standard or ' irq ' fields . If both are changed at the same time , we
* prefer the standard field .
*
2011-09-05 07:42:25 +00:00
* We implement adaptive IRQ moderation , but use a different algorithm
* from that assumed in the definition of struct ethtool_coalesce .
* Therefore we do not use any of the adaptive moderation parameters
* in it .
*/
2008-04-27 12:55:59 +01:00
static int efx_ethtool_get_coalesce ( struct net_device * net_dev ,
struct ethtool_coalesce * coalesce )
{
2008-09-01 12:43:14 +01:00
struct efx_nic * efx = netdev_priv ( net_dev ) ;
2011-09-05 07:42:25 +00:00
unsigned int tx_usecs , rx_usecs ;
bool rx_adaptive ;
2008-04-27 12:55:59 +01:00
2011-09-05 07:42:25 +00:00
efx_get_irq_moderation ( efx , & tx_usecs , & rx_usecs , & rx_adaptive ) ;
2008-04-27 12:55:59 +01:00
2011-09-05 07:43:49 +00:00
coalesce - > tx_coalesce_usecs = tx_usecs ;
2011-09-05 07:42:25 +00:00
coalesce - > tx_coalesce_usecs_irq = tx_usecs ;
2011-09-05 07:43:49 +00:00
coalesce - > rx_coalesce_usecs = rx_usecs ;
2011-09-05 07:42:25 +00:00
coalesce - > rx_coalesce_usecs_irq = rx_usecs ;
coalesce - > use_adaptive_rx_coalesce = rx_adaptive ;
2009-10-23 08:32:13 +00:00
2008-04-27 12:55:59 +01:00
return 0 ;
}
static int efx_ethtool_set_coalesce ( struct net_device * net_dev ,
struct ethtool_coalesce * coalesce )
{
2008-09-01 12:43:14 +01:00
struct efx_nic * efx = netdev_priv ( net_dev ) ;
2008-04-27 12:55:59 +01:00
struct efx_channel * channel ;
2011-09-05 07:41:44 +00:00
unsigned int tx_usecs , rx_usecs ;
2011-09-05 07:43:04 +00:00
bool adaptive , rx_may_override_tx ;
int rc ;
2008-04-27 12:55:59 +01:00
2009-03-20 13:30:37 +00:00
if ( coalesce - > use_adaptive_tx_coalesce )
2011-09-05 07:41:27 +00:00
return - EINVAL ;
2008-04-27 12:55:59 +01:00
2011-09-05 07:42:25 +00:00
efx_get_irq_moderation ( efx , & tx_usecs , & rx_usecs , & adaptive ) ;
2011-09-05 07:43:49 +00:00
if ( coalesce - > rx_coalesce_usecs ! = rx_usecs )
rx_usecs = coalesce - > rx_coalesce_usecs ;
else
rx_usecs = coalesce - > rx_coalesce_usecs_irq ;
2009-03-20 13:30:37 +00:00
adaptive = coalesce - > use_adaptive_rx_coalesce ;
2008-04-27 12:55:59 +01:00
2011-09-05 07:42:25 +00:00
/* If channels are shared, TX IRQ moderation can be quietly
* overridden unless it is changed from its old value .
*/
2011-09-05 07:43:49 +00:00
rx_may_override_tx = ( coalesce - > tx_coalesce_usecs = = tx_usecs & &
coalesce - > tx_coalesce_usecs_irq = = tx_usecs ) ;
if ( coalesce - > tx_coalesce_usecs ! = tx_usecs )
tx_usecs = coalesce - > tx_coalesce_usecs ;
else
tx_usecs = coalesce - > tx_coalesce_usecs_irq ;
2008-04-27 12:55:59 +01:00
2011-09-05 07:43:04 +00:00
rc = efx_init_irq_moderation ( efx , tx_usecs , rx_usecs , adaptive ,
rx_may_override_tx ) ;
if ( rc ! = 0 )
return rc ;
2008-04-27 12:55:59 +01:00
efx_for_each_channel ( channel , efx )
2009-11-29 03:42:31 +00:00
efx - > type - > push_irq_moderation ( channel ) ;
2008-04-27 12:55:59 +01:00
return 0 ;
}
2010-09-10 06:42:33 +00:00
static void efx_ethtool_get_ringparam ( struct net_device * net_dev ,
struct ethtool_ringparam * ring )
{
struct efx_nic * efx = netdev_priv ( net_dev ) ;
ring - > rx_max_pending = EFX_MAX_DMAQ_SIZE ;
ring - > tx_max_pending = EFX_MAX_DMAQ_SIZE ;
ring - > rx_pending = efx - > rxq_entries ;
ring - > tx_pending = efx - > txq_entries ;
}
static int efx_ethtool_set_ringparam ( struct net_device * net_dev ,
struct ethtool_ringparam * ring )
{
struct efx_nic * efx = netdev_priv ( net_dev ) ;
if ( ring - > rx_mini_pending | | ring - > rx_jumbo_pending | |
ring - > rx_pending > EFX_MAX_DMAQ_SIZE | |
ring - > tx_pending > EFX_MAX_DMAQ_SIZE )
return - EINVAL ;
if ( ring - > rx_pending < EFX_MIN_RING_SIZE | |
ring - > tx_pending < EFX_MIN_RING_SIZE ) {
netif_err ( efx , drv , efx - > net_dev ,
" TX and RX queues cannot be smaller than %ld \n " ,
EFX_MIN_RING_SIZE ) ;
return - EINVAL ;
}
return efx_realloc_channels ( efx , ring - > rx_pending , ring - > tx_pending ) ;
}
2008-04-27 12:55:59 +01:00
static int efx_ethtool_set_pauseparam ( struct net_device * net_dev ,
struct ethtool_pauseparam * pause )
{
2008-09-01 12:43:14 +01:00
struct efx_nic * efx = netdev_priv ( net_dev ) ;
2011-05-17 17:53:22 -04:00
u8 wanted_fc , old_fc ;
2009-11-29 03:42:41 +00:00
u32 old_adv ;
2008-12-12 21:50:46 -08:00
bool reset ;
2009-11-29 03:42:41 +00:00
int rc = 0 ;
mutex_lock ( & efx - > mac_lock ) ;
2008-04-27 12:55:59 +01:00
2008-12-12 21:50:46 -08:00
wanted_fc = ( ( pause - > rx_pause ? EFX_FC_RX : 0 ) |
( pause - > tx_pause ? EFX_FC_TX : 0 ) |
( pause - > autoneg ? EFX_FC_AUTO : 0 ) ) ;
if ( ( wanted_fc & EFX_FC_TX ) & & ! ( wanted_fc & EFX_FC_RX ) ) {
2010-06-23 11:30:07 +00:00
netif_dbg ( efx , drv , efx - > net_dev ,
" Flow control unsupported: tx ON rx OFF \n " ) ;
2009-11-29 03:42:41 +00:00
rc = - EINVAL ;
goto out ;
2008-12-12 21:50:46 -08:00
}
2009-11-29 03:42:41 +00:00
if ( ( wanted_fc & EFX_FC_AUTO ) & & ! efx - > link_advertising ) {
2010-06-23 11:30:07 +00:00
netif_dbg ( efx , drv , efx - > net_dev ,
" Autonegotiation is disabled \n " ) ;
2009-11-29 03:42:41 +00:00
rc = - EINVAL ;
goto out ;
2008-12-12 21:50:46 -08:00
}
/* TX flow control may automatically turn itself off if the
* link partner ( intermittently ) stops responding to pause
* frames . There isn ' t any indication that this has happened ,
* so the best we do is leave it up to the user to spot this
* and fix it be cycling transmit flow control on this end . */
reset = ( wanted_fc & EFX_FC_TX ) & & ! ( efx - > wanted_fc & EFX_FC_TX ) ;
if ( EFX_WORKAROUND_11482 ( efx ) & & reset ) {
2009-11-28 05:36:04 +00:00
if ( efx_nic_rev ( efx ) = = EFX_REV_FALCON_B0 ) {
2008-12-12 21:50:46 -08:00
/* Recover by resetting the EM block */
2009-11-29 03:42:41 +00:00
falcon_stop_nic_stats ( efx ) ;
falcon_drain_tx_fifo ( efx ) ;
2011-09-03 00:15:00 +01:00
falcon_reconfigure_xmac ( efx ) ;
2009-11-29 03:42:41 +00:00
falcon_start_nic_stats ( efx ) ;
2008-12-12 21:50:46 -08:00
} else {
/* Schedule a reset to recover */
efx_schedule_reset ( efx , RESET_TYPE_INVISIBLE ) ;
}
}
2009-11-29 03:42:41 +00:00
old_adv = efx - > link_advertising ;
old_fc = efx - > wanted_fc ;
efx_link_set_wanted_fc ( efx , wanted_fc ) ;
if ( efx - > link_advertising ! = old_adv | |
( efx - > wanted_fc ^ old_fc ) & EFX_FC_AUTO ) {
rc = efx - > phy_op - > reconfigure ( efx ) ;
if ( rc ) {
2010-06-23 11:30:07 +00:00
netif_err ( efx , drv , efx - > net_dev ,
" Unable to advertise requested flow "
" control setting \n " ) ;
2009-11-29 03:42:41 +00:00
goto out ;
}
}
2008-12-12 21:50:46 -08:00
2009-11-29 03:42:41 +00:00
/* Reconfigure the MAC. The PHY *may* generate a link state change event
* if the user just changed the advertised capabilities , but there ' s no
* harm doing this twice */
2011-09-03 00:15:00 +01:00
efx - > type - > reconfigure_mac ( efx ) ;
2008-12-12 21:50:46 -08:00
2009-11-29 03:42:41 +00:00
out :
2008-12-12 21:50:46 -08:00
mutex_unlock ( & efx - > mac_lock ) ;
2008-04-27 12:55:59 +01:00
2009-11-29 03:42:41 +00:00
return rc ;
2008-04-27 12:55:59 +01:00
}
static void efx_ethtool_get_pauseparam ( struct net_device * net_dev ,
struct ethtool_pauseparam * pause )
{
2008-09-01 12:43:14 +01:00
struct efx_nic * efx = netdev_priv ( net_dev ) ;
2008-04-27 12:55:59 +01:00
2008-12-12 21:50:46 -08:00
pause - > rx_pause = ! ! ( efx - > wanted_fc & EFX_FC_RX ) ;
pause - > tx_pause = ! ! ( efx - > wanted_fc & EFX_FC_TX ) ;
pause - > autoneg = ! ! ( efx - > wanted_fc & EFX_FC_AUTO ) ;
2008-04-27 12:55:59 +01:00
}
2009-11-29 03:43:07 +00:00
static void efx_ethtool_get_wol ( struct net_device * net_dev ,
struct ethtool_wolinfo * wol )
{
struct efx_nic * efx = netdev_priv ( net_dev ) ;
return efx - > type - > get_wol ( efx , wol ) ;
}
static int efx_ethtool_set_wol ( struct net_device * net_dev ,
struct ethtool_wolinfo * wol )
{
struct efx_nic * efx = netdev_priv ( net_dev ) ;
return efx - > type - > set_wol ( efx , wol - > wolopts ) ;
}
2010-10-18 05:27:31 +00:00
static int efx_ethtool_reset ( struct net_device * net_dev , u32 * flags )
2009-11-29 03:43:15 +00:00
{
struct efx_nic * efx = netdev_priv ( net_dev ) ;
2011-06-24 20:50:07 +01:00
int rc ;
2009-11-29 03:43:15 +00:00
2011-06-24 20:50:07 +01:00
rc = efx - > type - > map_reset_flags ( flags ) ;
if ( rc < 0 )
return rc ;
2009-11-29 03:43:15 +00:00
2011-06-24 20:50:07 +01:00
return efx_reset ( efx , rc ) ;
2009-11-29 03:43:15 +00:00
}
2012-02-02 22:41:49 +00:00
/* MAC address mask including only MC flag */
static const u8 mac_addr_mc_mask [ ETH_ALEN ] = { 0x01 , 0 , 0 , 0 , 0 , 0 } ;
2012-01-03 12:05:47 +00:00
static int efx_ethtool_get_class_rule ( struct efx_nic * efx ,
struct ethtool_rx_flow_spec * rule )
{
struct ethtool_tcpip4_spec * ip_entry = & rule - > h_u . tcp_ip4_spec ;
struct ethtool_tcpip4_spec * ip_mask = & rule - > m_u . tcp_ip4_spec ;
2012-02-02 22:41:49 +00:00
struct ethhdr * mac_entry = & rule - > h_u . ether_spec ;
struct ethhdr * mac_mask = & rule - > m_u . ether_spec ;
2012-01-03 12:05:47 +00:00
struct efx_filter_spec spec ;
u16 vid ;
u8 proto ;
int rc ;
rc = efx_filter_get_filter_safe ( efx , EFX_FILTER_PRI_MANUAL ,
rule - > location , & spec ) ;
if ( rc )
return rc ;
if ( spec . dmaq_id = = 0xfff )
rule - > ring_cookie = RX_CLS_FLOW_DISC ;
else
rule - > ring_cookie = spec . dmaq_id ;
2012-02-02 22:41:49 +00:00
if ( spec . type = = EFX_FILTER_MC_DEF | | spec . type = = EFX_FILTER_UC_DEF ) {
rule - > flow_type = ETHER_FLOW ;
memcpy ( mac_mask - > h_dest , mac_addr_mc_mask , ETH_ALEN ) ;
if ( spec . type = = EFX_FILTER_MC_DEF )
memcpy ( mac_entry - > h_dest , mac_addr_mc_mask , ETH_ALEN ) ;
return 0 ;
}
rc = efx_filter_get_eth_local ( & spec , & vid , mac_entry - > h_dest ) ;
2012-01-03 12:05:47 +00:00
if ( rc = = 0 ) {
rule - > flow_type = ETHER_FLOW ;
2012-02-02 22:41:49 +00:00
memset ( mac_mask - > h_dest , ~ 0 , ETH_ALEN ) ;
2012-01-03 12:05:47 +00:00
if ( vid ! = EFX_FILTER_VID_UNSPEC ) {
rule - > flow_type | = FLOW_EXT ;
rule - > h_ext . vlan_tci = htons ( vid ) ;
rule - > m_ext . vlan_tci = htons ( 0xfff ) ;
}
return 0 ;
}
rc = efx_filter_get_ipv4_local ( & spec , & proto ,
& ip_entry - > ip4dst , & ip_entry - > pdst ) ;
if ( rc ! = 0 ) {
rc = efx_filter_get_ipv4_full (
& spec , & proto , & ip_entry - > ip4src , & ip_entry - > psrc ,
& ip_entry - > ip4dst , & ip_entry - > pdst ) ;
EFX_WARN_ON_PARANOID ( rc ) ;
ip_mask - > ip4src = ~ 0 ;
ip_mask - > psrc = ~ 0 ;
}
rule - > flow_type = ( proto = = IPPROTO_TCP ) ? TCP_V4_FLOW : UDP_V4_FLOW ;
ip_mask - > ip4dst = ~ 0 ;
ip_mask - > pdst = ~ 0 ;
return rc ;
}
2010-06-30 05:06:28 +00:00
static int
efx_ethtool_get_rxnfc ( struct net_device * net_dev ,
2012-01-03 12:05:47 +00:00
struct ethtool_rxnfc * info , u32 * rule_locs )
2010-06-30 05:06:28 +00:00
{
struct efx_nic * efx = netdev_priv ( net_dev ) ;
switch ( info - > cmd ) {
case ETHTOOL_GRXRINGS :
info - > data = efx - > n_rx_channels ;
return 0 ;
case ETHTOOL_GRXFH : {
unsigned min_revision = 0 ;
info - > data = 0 ;
switch ( info - > flow_type ) {
case TCP_V4_FLOW :
info - > data | = RXH_L4_B_0_1 | RXH_L4_B_2_3 ;
/* fall through */
case UDP_V4_FLOW :
case SCTP_V4_FLOW :
case AH_ESP_V4_FLOW :
case IPV4_FLOW :
info - > data | = RXH_IP_SRC | RXH_IP_DST ;
min_revision = EFX_REV_FALCON_B0 ;
break ;
case TCP_V6_FLOW :
info - > data | = RXH_L4_B_0_1 | RXH_L4_B_2_3 ;
/* fall through */
case UDP_V6_FLOW :
case SCTP_V6_FLOW :
case AH_ESP_V6_FLOW :
case IPV6_FLOW :
info - > data | = RXH_IP_SRC | RXH_IP_DST ;
min_revision = EFX_REV_SIENA_A0 ;
break ;
default :
break ;
}
if ( efx_nic_rev ( efx ) < min_revision )
info - > data = 0 ;
return 0 ;
}
2012-01-03 12:05:47 +00:00
case ETHTOOL_GRXCLSRLCNT :
info - > data = efx_filter_get_rx_id_limit ( efx ) ;
if ( info - > data = = 0 )
return - EOPNOTSUPP ;
info - > data | = RX_CLS_LOC_SPECIAL ;
info - > rule_cnt =
efx_filter_count_rx_used ( efx , EFX_FILTER_PRI_MANUAL ) ;
return 0 ;
case ETHTOOL_GRXCLSRULE :
if ( efx_filter_get_rx_id_limit ( efx ) = = 0 )
return - EOPNOTSUPP ;
return efx_ethtool_get_class_rule ( efx , & info - > fs ) ;
case ETHTOOL_GRXCLSRLALL : {
s32 rc ;
info - > data = efx_filter_get_rx_id_limit ( efx ) ;
if ( info - > data = = 0 )
return - EOPNOTSUPP ;
rc = efx_filter_get_rx_ids ( efx , EFX_FILTER_PRI_MANUAL ,
rule_locs , info - > rule_cnt ) ;
if ( rc < 0 )
return rc ;
info - > rule_cnt = rc ;
return 0 ;
}
2010-06-30 05:06:28 +00:00
default :
return - EOPNOTSUPP ;
}
}
2012-01-03 12:05:47 +00:00
static int efx_ethtool_set_class_rule ( struct efx_nic * efx ,
struct ethtool_rx_flow_spec * rule )
2010-09-20 08:43:42 +00:00
{
2012-01-03 12:05:47 +00:00
struct ethtool_tcpip4_spec * ip_entry = & rule - > h_u . tcp_ip4_spec ;
struct ethtool_tcpip4_spec * ip_mask = & rule - > m_u . tcp_ip4_spec ;
struct ethhdr * mac_entry = & rule - > h_u . ether_spec ;
struct ethhdr * mac_mask = & rule - > m_u . ether_spec ;
struct efx_filter_spec spec ;
2010-12-07 19:11:26 +00:00
int rc ;
2010-09-20 08:43:42 +00:00
2012-01-03 12:05:47 +00:00
/* Check that user wants us to choose the location */
if ( rule - > location ! = RX_CLS_LOC_ANY & &
rule - > location ! = RX_CLS_LOC_FIRST & &
rule - > location ! = RX_CLS_LOC_LAST )
2010-09-20 08:43:42 +00:00
return - EINVAL ;
2012-01-03 12:05:47 +00:00
/* Range-check ring_cookie */
if ( rule - > ring_cookie > = efx - > n_rx_channels & &
rule - > ring_cookie ! = RX_CLS_FLOW_DISC )
2010-09-20 08:43:42 +00:00
return - EINVAL ;
2012-01-03 12:05:47 +00:00
/* Check for unsupported extensions */
if ( ( rule - > flow_type & FLOW_EXT ) & &
( rule - > m_ext . vlan_etype | rule - > m_ext . data [ 0 ] |
rule - > m_ext . data [ 1 ] ) )
return - EINVAL ;
efx_filter_init_rx ( & spec , EFX_FILTER_PRI_MANUAL ,
( rule - > location = = RX_CLS_LOC_FIRST ) ?
EFX_FILTER_FLAG_RX_OVERRIDE_IP : 0 ,
( rule - > ring_cookie = = RX_CLS_FLOW_DISC ) ?
0xfff : rule - > ring_cookie ) ;
2010-12-07 19:11:26 +00:00
2012-01-03 12:05:47 +00:00
switch ( rule - > flow_type ) {
2010-09-20 08:43:42 +00:00
case TCP_V4_FLOW :
2010-12-07 19:11:26 +00:00
case UDP_V4_FLOW : {
2012-01-03 12:05:47 +00:00
u8 proto = ( rule - > flow_type = = TCP_V4_FLOW ?
2010-12-07 19:11:26 +00:00
IPPROTO_TCP : IPPROTO_UDP ) ;
2010-09-20 08:43:42 +00:00
/* Must match all of destination, */
2012-01-03 12:05:47 +00:00
if ( ( __force u32 ) ~ ip_mask - > ip4dst |
( __force u16 ) ~ ip_mask - > pdst )
2010-09-20 08:43:42 +00:00
return - EINVAL ;
/* all or none of source, */
if ( ( ip_mask - > ip4src | ip_mask - > psrc ) & &
( ( __force u32 ) ~ ip_mask - > ip4src |
( __force u16 ) ~ ip_mask - > psrc ) )
return - EINVAL ;
/* and nothing else */
2012-01-03 12:05:47 +00:00
if ( ip_mask - > tos | rule - > m_ext . vlan_tci )
2010-09-20 08:43:42 +00:00
return - EINVAL ;
2010-12-07 19:11:26 +00:00
2012-01-03 12:05:47 +00:00
if ( ip_mask - > ip4src )
rc = efx_filter_set_ipv4_full ( & spec , proto ,
2010-12-07 19:11:26 +00:00
ip_entry - > ip4dst ,
ip_entry - > pdst ,
ip_entry - > ip4src ,
ip_entry - > psrc ) ;
else
2012-01-03 12:05:47 +00:00
rc = efx_filter_set_ipv4_local ( & spec , proto ,
2010-12-07 19:11:26 +00:00
ip_entry - > ip4dst ,
ip_entry - > pdst ) ;
if ( rc )
return rc ;
2010-09-20 08:43:42 +00:00
break ;
2010-12-07 19:11:26 +00:00
}
2012-01-03 12:05:47 +00:00
case ETHER_FLOW | FLOW_EXT :
2012-02-02 22:41:49 +00:00
case ETHER_FLOW : {
u16 vlan_tag_mask = ( rule - > flow_type & FLOW_EXT ?
ntohs ( rule - > m_ext . vlan_tci ) : 0 ) ;
/* Must not match on source address or Ethertype */
2012-01-03 12:05:47 +00:00
if ( ! is_zero_ether_addr ( mac_mask - > h_source ) | |
mac_mask - > h_proto )
2010-09-20 08:43:42 +00:00
return - EINVAL ;
2010-12-07 19:11:26 +00:00
2012-02-02 22:41:49 +00:00
/* Is it a default UC or MC filter? */
if ( ! compare_ether_addr ( mac_mask - > h_dest , mac_addr_mc_mask ) & &
vlan_tag_mask = = 0 ) {
if ( is_multicast_ether_addr ( mac_entry - > h_dest ) )
rc = efx_filter_set_mc_def ( & spec ) ;
else
rc = efx_filter_set_uc_def ( & spec ) ;
}
/* Otherwise, it must match all of destination and all
* or none of VID .
*/
else if ( is_broadcast_ether_addr ( mac_mask - > h_dest ) & &
( vlan_tag_mask = = 0xfff | | vlan_tag_mask = = 0 ) ) {
rc = efx_filter_set_eth_local (
& spec ,
vlan_tag_mask ?
ntohs ( rule - > h_ext . vlan_tci ) : EFX_FILTER_VID_UNSPEC ,
mac_entry - > h_dest ) ;
} else {
rc = - EINVAL ;
}
2010-12-07 19:11:26 +00:00
if ( rc )
return rc ;
2010-09-20 08:43:42 +00:00
break ;
2012-02-02 22:41:49 +00:00
}
2010-12-07 19:11:26 +00:00
2010-09-20 08:43:42 +00:00
default :
return - EINVAL ;
}
2012-01-03 12:05:47 +00:00
rc = efx_filter_insert_filter ( efx , & spec , true ) ;
if ( rc < 0 )
return rc ;
2011-05-14 02:35:25 +01:00
2012-01-03 12:05:47 +00:00
rule - > location = rc ;
return 0 ;
}
static int efx_ethtool_set_rxnfc ( struct net_device * net_dev ,
struct ethtool_rxnfc * info )
{
struct efx_nic * efx = netdev_priv ( net_dev ) ;
if ( efx_filter_get_rx_id_limit ( efx ) = = 0 )
return - EOPNOTSUPP ;
switch ( info - > cmd ) {
case ETHTOOL_SRXCLSRLINS :
return efx_ethtool_set_class_rule ( efx , & info - > fs ) ;
case ETHTOOL_SRXCLSRLDEL :
return efx_filter_remove_id_safe ( efx , EFX_FILTER_PRI_MANUAL ,
info - > fs . location ) ;
default :
return - EOPNOTSUPP ;
}
2010-09-20 08:43:42 +00:00
}
2011-12-15 13:55:01 +00:00
static u32 efx_ethtool_get_rxfh_indir_size ( struct net_device * net_dev )
2010-06-30 05:06:28 +00:00
{
struct efx_nic * efx = netdev_priv ( net_dev ) ;
2011-12-15 13:55:01 +00:00
return ( efx_nic_rev ( efx ) < EFX_REV_FALCON_B0 ?
0 : ARRAY_SIZE ( efx - > rx_indir_table ) ) ;
}
static int efx_ethtool_get_rxfh_indir ( struct net_device * net_dev , u32 * indir )
{
struct efx_nic * efx = netdev_priv ( net_dev ) ;
2010-06-30 05:06:28 +00:00
2011-12-15 13:55:01 +00:00
memcpy ( indir , efx - > rx_indir_table , sizeof ( efx - > rx_indir_table ) ) ;
2010-06-30 05:06:28 +00:00
return 0 ;
}
static int efx_ethtool_set_rxfh_indir ( struct net_device * net_dev ,
2011-12-15 13:55:01 +00:00
const u32 * indir )
2010-06-30 05:06:28 +00:00
{
struct efx_nic * efx = netdev_priv ( net_dev ) ;
2011-12-15 13:55:01 +00:00
memcpy ( efx - > rx_indir_table , indir , sizeof ( efx - > rx_indir_table ) ) ;
2010-06-30 05:06:28 +00:00
efx_nic_push_rx_indir_table ( efx ) ;
return 0 ;
}
2009-09-02 01:03:33 -07:00
const struct ethtool_ops efx_ethtool_ops = {
2008-04-27 12:55:59 +01:00
. get_settings = efx_ethtool_get_settings ,
. set_settings = efx_ethtool_set_settings ,
. get_drvinfo = efx_ethtool_get_drvinfo ,
2010-06-21 03:06:53 +00:00
. get_regs_len = efx_ethtool_get_regs_len ,
. get_regs = efx_ethtool_get_regs ,
2010-06-23 11:30:07 +00:00
. get_msglevel = efx_ethtool_get_msglevel ,
. set_msglevel = efx_ethtool_set_msglevel ,
2008-04-27 12:55:59 +01:00
. nway_reset = efx_ethtool_nway_reset ,
2010-12-09 12:10:25 +00:00
. get_link = ethtool_op_get_link ,
2008-04-27 12:55:59 +01:00
. get_coalesce = efx_ethtool_get_coalesce ,
. set_coalesce = efx_ethtool_set_coalesce ,
2010-09-10 06:42:33 +00:00
. get_ringparam = efx_ethtool_get_ringparam ,
. set_ringparam = efx_ethtool_set_ringparam ,
2008-04-27 12:55:59 +01:00
. get_pauseparam = efx_ethtool_get_pauseparam ,
. set_pauseparam = efx_ethtool_set_pauseparam ,
2008-09-01 12:48:12 +01:00
. get_sset_count = efx_ethtool_get_sset_count ,
2008-05-07 13:36:19 +01:00
. self_test = efx_ethtool_self_test ,
2008-04-27 12:55:59 +01:00
. get_strings = efx_ethtool_get_strings ,
2011-04-02 00:43:46 +01:00
. set_phys_id = efx_ethtool_phys_id ,
2008-04-27 12:55:59 +01:00
. get_ethtool_stats = efx_ethtool_get_stats ,
2009-11-29 03:43:07 +00:00
. get_wol = efx_ethtool_get_wol ,
. set_wol = efx_ethtool_set_wol ,
2009-11-29 03:43:15 +00:00
. reset = efx_ethtool_reset ,
2010-06-30 05:06:28 +00:00
. get_rxnfc = efx_ethtool_get_rxnfc ,
2012-01-03 12:05:47 +00:00
. set_rxnfc = efx_ethtool_set_rxnfc ,
2011-12-15 13:55:01 +00:00
. get_rxfh_indir_size = efx_ethtool_get_rxfh_indir_size ,
2010-06-30 05:06:28 +00:00
. get_rxfh_indir = efx_ethtool_get_rxfh_indir ,
. set_rxfh_indir = efx_ethtool_set_rxfh_indir ,
2008-04-27 12:55:59 +01:00
} ;