2009-10-14 15:13:45 -07:00
/*******************************************************************************
STMMAC Ethtool support
Copyright ( C ) 2007 - 2009 STMicroelectronics Ltd
This program is free software ; you can redistribute it and / or modify it
under the terms and conditions of the GNU General Public License ,
version 2 , as published by the Free Software Foundation .
This program is distributed in the hope 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 .
You should have received a copy of the GNU General Public License along with
this program ; if not , write to the Free Software Foundation , Inc . ,
51 Franklin St - Fifth Floor , Boston , MA 02110 - 1301 USA .
The full GNU General Public License is included in this distribution in
the file called " COPYING " .
Author : Giuseppe Cavallaro < peppe . cavallaro @ st . com >
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include <linux/etherdevice.h>
# include <linux/ethtool.h>
# include <linux/mii.h>
# include <linux/phy.h>
# include "stmmac.h"
2010-01-06 23:07:18 +00:00
# include "dwmac_dma.h"
2009-10-14 15:13:45 -07:00
# define REG_SPACE_SIZE 0x1054
# define MAC100_ETHTOOL_NAME "st_mac100"
# define GMAC_ETHTOOL_NAME "st_gmac"
struct stmmac_stats {
char stat_string [ ETH_GSTRING_LEN ] ;
int sizeof_stat ;
int stat_offset ;
} ;
# define STMMAC_STAT(m) \
{ # m , FIELD_SIZEOF ( struct stmmac_extra_stats , m ) , \
offsetof ( struct stmmac_priv , xstats . m ) }
static const struct stmmac_stats stmmac_gstrings_stats [ ] = {
STMMAC_STAT ( tx_underflow ) ,
STMMAC_STAT ( tx_carrier ) ,
STMMAC_STAT ( tx_losscarrier ) ,
STMMAC_STAT ( tx_heartbeat ) ,
STMMAC_STAT ( tx_deferred ) ,
STMMAC_STAT ( tx_vlan ) ,
STMMAC_STAT ( rx_vlan ) ,
STMMAC_STAT ( tx_jabber ) ,
STMMAC_STAT ( tx_frame_flushed ) ,
STMMAC_STAT ( tx_payload_error ) ,
STMMAC_STAT ( tx_ip_header_error ) ,
STMMAC_STAT ( rx_desc ) ,
STMMAC_STAT ( rx_partial ) ,
STMMAC_STAT ( rx_runt ) ,
STMMAC_STAT ( rx_toolong ) ,
STMMAC_STAT ( rx_collision ) ,
STMMAC_STAT ( rx_crc ) ,
2010-02-04 09:33:21 -08:00
STMMAC_STAT ( rx_length ) ,
2009-10-14 15:13:45 -07:00
STMMAC_STAT ( rx_mii ) ,
STMMAC_STAT ( rx_multicast ) ,
STMMAC_STAT ( rx_gmac_overflow ) ,
STMMAC_STAT ( rx_watchdog ) ,
STMMAC_STAT ( da_rx_filter_fail ) ,
STMMAC_STAT ( sa_rx_filter_fail ) ,
STMMAC_STAT ( rx_missed_cntr ) ,
STMMAC_STAT ( rx_overflow_cntr ) ,
STMMAC_STAT ( tx_undeflow_irq ) ,
STMMAC_STAT ( tx_process_stopped_irq ) ,
STMMAC_STAT ( tx_jabber_irq ) ,
STMMAC_STAT ( rx_overflow_irq ) ,
STMMAC_STAT ( rx_buf_unav_irq ) ,
STMMAC_STAT ( rx_process_stopped_irq ) ,
STMMAC_STAT ( rx_watchdog_irq ) ,
STMMAC_STAT ( tx_early_irq ) ,
STMMAC_STAT ( fatal_bus_error_irq ) ,
STMMAC_STAT ( threshold ) ,
STMMAC_STAT ( tx_pkt_n ) ,
STMMAC_STAT ( rx_pkt_n ) ,
STMMAC_STAT ( poll_n ) ,
STMMAC_STAT ( sched_timer_n ) ,
STMMAC_STAT ( normal_irq_n ) ,
} ;
# define STMMAC_STATS_LEN ARRAY_SIZE(stmmac_gstrings_stats)
2010-10-13 14:50:31 +00:00
static void stmmac_ethtool_getdrvinfo ( struct net_device * dev ,
struct ethtool_drvinfo * info )
2009-10-14 15:13:45 -07:00
{
struct stmmac_priv * priv = netdev_priv ( dev ) ;
2010-11-24 02:37:58 +00:00
if ( ! priv - > plat - > has_gmac )
2009-10-14 15:13:45 -07:00
strcpy ( info - > driver , MAC100_ETHTOOL_NAME ) ;
else
strcpy ( info - > driver , GMAC_ETHTOOL_NAME ) ;
strcpy ( info - > version , DRV_MODULE_VERSION ) ;
info - > fw_version [ 0 ] = ' \0 ' ;
info - > n_stats = STMMAC_STATS_LEN ;
}
2010-10-13 14:50:31 +00:00
static int stmmac_ethtool_getsettings ( struct net_device * dev ,
struct ethtool_cmd * cmd )
2009-10-14 15:13:45 -07:00
{
struct stmmac_priv * priv = netdev_priv ( dev ) ;
struct phy_device * phy = priv - > phydev ;
int rc ;
if ( phy = = NULL ) {
pr_err ( " %s: %s: PHY is not registered \n " ,
__func__ , dev - > name ) ;
return - ENODEV ;
}
if ( ! netif_running ( dev ) ) {
pr_err ( " %s: interface is disabled: we cannot track "
" link speed / duplex setting \n " , dev - > name ) ;
return - EBUSY ;
}
cmd - > transceiver = XCVR_INTERNAL ;
spin_lock_irq ( & priv - > lock ) ;
rc = phy_ethtool_gset ( phy , cmd ) ;
spin_unlock_irq ( & priv - > lock ) ;
return rc ;
}
2010-10-13 14:50:31 +00:00
static int stmmac_ethtool_setsettings ( struct net_device * dev ,
struct ethtool_cmd * cmd )
2009-10-14 15:13:45 -07:00
{
struct stmmac_priv * priv = netdev_priv ( dev ) ;
struct phy_device * phy = priv - > phydev ;
int rc ;
spin_lock ( & priv - > lock ) ;
rc = phy_ethtool_sset ( phy , cmd ) ;
spin_unlock ( & priv - > lock ) ;
return rc ;
}
2010-10-13 14:50:31 +00:00
static u32 stmmac_ethtool_getmsglevel ( struct net_device * dev )
2009-10-14 15:13:45 -07:00
{
struct stmmac_priv * priv = netdev_priv ( dev ) ;
return priv - > msg_enable ;
}
2010-10-13 14:50:31 +00:00
static void stmmac_ethtool_setmsglevel ( struct net_device * dev , u32 level )
2009-10-14 15:13:45 -07:00
{
struct stmmac_priv * priv = netdev_priv ( dev ) ;
priv - > msg_enable = level ;
}
2010-10-13 14:50:31 +00:00
static int stmmac_check_if_running ( struct net_device * dev )
2009-10-14 15:13:45 -07:00
{
if ( ! netif_running ( dev ) )
return - EBUSY ;
return 0 ;
}
2010-10-13 14:50:31 +00:00
static int stmmac_ethtool_get_regs_len ( struct net_device * dev )
2009-10-14 15:13:45 -07:00
{
return REG_SPACE_SIZE ;
}
2010-10-13 14:50:31 +00:00
static void stmmac_ethtool_gregs ( struct net_device * dev ,
2009-10-14 15:13:45 -07:00
struct ethtool_regs * regs , void * space )
{
int i ;
u32 * reg_space = ( u32 * ) space ;
struct stmmac_priv * priv = netdev_priv ( dev ) ;
memset ( reg_space , 0x0 , REG_SPACE_SIZE ) ;
2010-11-24 02:37:58 +00:00
if ( ! priv - > plat - > has_gmac ) {
2009-10-14 15:13:45 -07:00
/* MAC registers */
for ( i = 0 ; i < 12 ; i + + )
2010-08-23 20:40:42 +00:00
reg_space [ i ] = readl ( priv - > ioaddr + ( i * 4 ) ) ;
2009-10-14 15:13:45 -07:00
/* DMA registers */
for ( i = 0 ; i < 9 ; i + + )
reg_space [ i + 12 ] =
2010-08-23 20:40:42 +00:00
readl ( priv - > ioaddr + ( DMA_BUS_MODE + ( i * 4 ) ) ) ;
reg_space [ 22 ] = readl ( priv - > ioaddr + DMA_CUR_TX_BUF_ADDR ) ;
reg_space [ 23 ] = readl ( priv - > ioaddr + DMA_CUR_RX_BUF_ADDR ) ;
2009-10-14 15:13:45 -07:00
} else {
/* MAC registers */
for ( i = 0 ; i < 55 ; i + + )
2010-08-23 20:40:42 +00:00
reg_space [ i ] = readl ( priv - > ioaddr + ( i * 4 ) ) ;
2009-10-14 15:13:45 -07:00
/* DMA registers */
for ( i = 0 ; i < 22 ; i + + )
reg_space [ i + 55 ] =
2010-08-23 20:40:42 +00:00
readl ( priv - > ioaddr + ( DMA_BUS_MODE + ( i * 4 ) ) ) ;
2009-10-14 15:13:45 -07:00
}
}
static void
stmmac_get_pauseparam ( struct net_device * netdev ,
struct ethtool_pauseparam * pause )
{
struct stmmac_priv * priv = netdev_priv ( netdev ) ;
spin_lock ( & priv - > lock ) ;
pause - > rx_pause = 0 ;
pause - > tx_pause = 0 ;
pause - > autoneg = priv - > phydev - > autoneg ;
if ( priv - > flow_ctrl & FLOW_RX )
pause - > rx_pause = 1 ;
if ( priv - > flow_ctrl & FLOW_TX )
pause - > tx_pause = 1 ;
spin_unlock ( & priv - > lock ) ;
}
static int
stmmac_set_pauseparam ( struct net_device * netdev ,
struct ethtool_pauseparam * pause )
{
struct stmmac_priv * priv = netdev_priv ( netdev ) ;
struct phy_device * phy = priv - > phydev ;
int new_pause = FLOW_OFF ;
int ret = 0 ;
spin_lock ( & priv - > lock ) ;
if ( pause - > rx_pause )
new_pause | = FLOW_RX ;
if ( pause - > tx_pause )
new_pause | = FLOW_TX ;
priv - > flow_ctrl = new_pause ;
2011-05-12 20:28:05 +00:00
phy - > autoneg = pause - > autoneg ;
2009-10-14 15:13:45 -07:00
if ( phy - > autoneg ) {
2011-05-12 20:28:04 +00:00
if ( netif_running ( netdev ) )
ret = phy_start_aneg ( phy ) ;
2010-08-23 20:40:42 +00:00
} else
priv - > hw - > mac - > flow_ctrl ( priv - > ioaddr , phy - > duplex ,
2010-01-06 23:07:17 +00:00
priv - > flow_ctrl , priv - > pause ) ;
2009-10-14 15:13:45 -07:00
spin_unlock ( & priv - > lock ) ;
return ret ;
}
static void stmmac_get_ethtool_stats ( struct net_device * dev ,
struct ethtool_stats * dummy , u64 * data )
{
struct stmmac_priv * priv = netdev_priv ( dev ) ;
int i ;
/* Update HW stats if supported */
2010-01-06 23:07:17 +00:00
priv - > hw - > dma - > dma_diagnostic_fr ( & dev - > stats , ( void * ) & priv - > xstats ,
2010-08-23 20:40:42 +00:00
priv - > ioaddr ) ;
2009-10-14 15:13:45 -07:00
for ( i = 0 ; i < STMMAC_STATS_LEN ; i + + ) {
char * p = ( char * ) priv + stmmac_gstrings_stats [ i ] . stat_offset ;
data [ i ] = ( stmmac_gstrings_stats [ i ] . sizeof_stat = =
sizeof ( u64 ) ) ? ( * ( u64 * ) p ) : ( * ( u32 * ) p ) ;
}
}
static int stmmac_get_sset_count ( struct net_device * netdev , int sset )
{
switch ( sset ) {
case ETH_SS_STATS :
return STMMAC_STATS_LEN ;
default :
return - EOPNOTSUPP ;
}
}
static void stmmac_get_strings ( struct net_device * dev , u32 stringset , u8 * data )
{
int i ;
u8 * p = data ;
switch ( stringset ) {
case ETH_SS_STATS :
for ( i = 0 ; i < STMMAC_STATS_LEN ; i + + ) {
memcpy ( p , stmmac_gstrings_stats [ i ] . stat_string ,
ETH_GSTRING_LEN ) ;
p + = ETH_GSTRING_LEN ;
}
break ;
default :
WARN_ON ( 1 ) ;
break ;
}
}
/* Currently only support WOL through Magic packet. */
static void stmmac_get_wol ( struct net_device * dev , struct ethtool_wolinfo * wol )
{
struct stmmac_priv * priv = netdev_priv ( dev ) ;
spin_lock_irq ( & priv - > lock ) ;
2010-09-24 21:27:41 -07:00
if ( device_can_wakeup ( priv - > device ) ) {
2011-04-13 11:51:43 -07:00
wol - > supported = WAKE_MAGIC | WAKE_UCAST ;
2009-10-14 15:13:45 -07:00
wol - > wolopts = priv - > wolopts ;
}
spin_unlock_irq ( & priv - > lock ) ;
}
static int stmmac_set_wol ( struct net_device * dev , struct ethtool_wolinfo * wol )
{
struct stmmac_priv * priv = netdev_priv ( dev ) ;
2011-04-13 11:51:43 -07:00
u32 support = WAKE_MAGIC | WAKE_UCAST ;
2009-10-14 15:13:45 -07:00
2010-09-24 21:27:41 -07:00
if ( ! device_can_wakeup ( priv - > device ) )
2009-10-14 15:13:45 -07:00
return - EINVAL ;
if ( wol - > wolopts & ~ support )
return - EINVAL ;
2010-09-24 21:27:41 -07:00
if ( wol - > wolopts ) {
pr_info ( " stmmac: wakeup enable \n " ) ;
2009-10-14 15:13:45 -07:00
device_set_wakeup_enable ( priv - > device , 1 ) ;
2010-09-24 21:27:41 -07:00
enable_irq_wake ( dev - > irq ) ;
} else {
device_set_wakeup_enable ( priv - > device , 0 ) ;
disable_irq_wake ( dev - > irq ) ;
}
2009-10-14 15:13:45 -07:00
spin_lock_irq ( & priv - > lock ) ;
priv - > wolopts = wol - > wolopts ;
spin_unlock_irq ( & priv - > lock ) ;
return 0 ;
}
static struct ethtool_ops stmmac_ethtool_ops = {
. begin = stmmac_check_if_running ,
. get_drvinfo = stmmac_ethtool_getdrvinfo ,
. get_settings = stmmac_ethtool_getsettings ,
. set_settings = stmmac_ethtool_setsettings ,
. get_msglevel = stmmac_ethtool_getmsglevel ,
. set_msglevel = stmmac_ethtool_setmsglevel ,
. get_regs = stmmac_ethtool_gregs ,
. get_regs_len = stmmac_ethtool_get_regs_len ,
. get_link = ethtool_op_get_link ,
. get_pauseparam = stmmac_get_pauseparam ,
. set_pauseparam = stmmac_set_pauseparam ,
. get_ethtool_stats = stmmac_get_ethtool_stats ,
. get_strings = stmmac_get_strings ,
. get_wol = stmmac_get_wol ,
. set_wol = stmmac_set_wol ,
. get_sset_count = stmmac_get_sset_count ,
} ;
void stmmac_set_ethtool_ops ( struct net_device * netdev )
{
SET_ETHTOOL_OPS ( netdev , & stmmac_ethtool_ops ) ;
}