2007-07-19 07:47:47 +04:00
/*
* Copyright ( c ) 2007 Freescale Semiconductor , Inc . All rights reserved .
*
* Description : QE UCC Gigabit Ethernet Ethtool API Set
*
* Author : Li Yang < leoli @ freescale . com >
*
2008-05-13 09:41:58 +04:00
* Limitation :
2007-07-19 07:47:47 +04:00
* Can only get / set setttings of the first queue .
2009-04-22 20:21:29 +04:00
* Need to re - open the interface manually after changing some parameters .
2007-07-19 07:47:47 +04:00
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation ; either version 2 of the License , or ( at your
* option ) any later version .
*/
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/errno.h>
# include <linux/stddef.h>
# include <linux/interrupt.h>
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
# include <linux/skbuff.h>
# include <linux/spinlock.h>
# include <linux/mm.h>
# include <linux/delay.h>
# include <linux/dma-mapping.h>
# include <linux/ethtool.h>
# include <linux/mii.h>
# include <linux/phy.h>
# include <asm/io.h>
# include <asm/irq.h>
# include <asm/uaccess.h>
# include <asm/types.h>
# include "ucc_geth.h"
static char hw_stat_gstrings [ ] [ ETH_GSTRING_LEN ] = {
" tx-64-frames " ,
" tx-65-127-frames " ,
" tx-128-255-frames " ,
" rx-64-frames " ,
" rx-65-127-frames " ,
" rx-128-255-frames " ,
" tx-bytes-ok " ,
" tx-pause-frames " ,
" tx-multicast-frames " ,
" tx-broadcast-frames " ,
" rx-frames " ,
" rx-bytes-ok " ,
" rx-bytes-all " ,
" rx-multicast-frames " ,
" rx-broadcast-frames " ,
" stats-counter-carry " ,
" stats-counter-mask " ,
" rx-dropped-frames " ,
} ;
static char tx_fw_stat_gstrings [ ] [ ETH_GSTRING_LEN ] = {
" tx-single-collision " ,
" tx-multiple-collision " ,
" tx-late-collsion " ,
" tx-aborted-frames " ,
" tx-lost-frames " ,
" tx-carrier-sense-errors " ,
" tx-frames-ok " ,
" tx-excessive-differ-frames " ,
" tx-256-511-frames " ,
2008-05-23 14:11:27 +04:00
" tx-512-1023-frames " ,
2007-07-19 07:47:47 +04:00
" tx-1024-1518-frames " ,
" tx-jumbo-frames " ,
} ;
static char rx_fw_stat_gstrings [ ] [ ETH_GSTRING_LEN ] = {
" rx-crc-errors " ,
" rx-alignment-errors " ,
" rx-in-range-length-errors " ,
" rx-out-of-range-length-errors " ,
" rx-too-long-frames " ,
" rx-runt " ,
" rx-very-long-event " ,
" rx-symbol-errors " ,
" rx-busy-drop-frames " ,
" reserved " ,
" reserved " ,
" rx-mismatch-drop-frames " ,
" rx-small-than-64 " ,
" rx-256-511-frames " ,
" rx-512-1023-frames " ,
" rx-1024-1518-frames " ,
" rx-jumbo-frames " ,
" rx-mac-error-loss " ,
" rx-pause-frames " ,
" reserved " ,
" rx-vlan-removed " ,
" rx-vlan-replaced " ,
" rx-vlan-inserted " ,
" rx-ip-checksum-errors " ,
} ;
# define UEC_HW_STATS_LEN ARRAY_SIZE(hw_stat_gstrings)
# define UEC_TX_FW_STATS_LEN ARRAY_SIZE(tx_fw_stat_gstrings)
# define UEC_RX_FW_STATS_LEN ARRAY_SIZE(rx_fw_stat_gstrings)
static int
uec_get_settings ( struct net_device * netdev , struct ethtool_cmd * ecmd )
{
struct ucc_geth_private * ugeth = netdev_priv ( netdev ) ;
struct phy_device * phydev = ugeth - > phydev ;
struct ucc_geth_info * ug_info = ugeth - > ug_info ;
if ( ! phydev )
return - ENODEV ;
ecmd - > maxtxpkt = 1 ;
ecmd - > maxrxpkt = ug_info - > interruptcoalescingmaxvalue [ 0 ] ;
return phy_ethtool_gset ( phydev , ecmd ) ;
}
static int
uec_set_settings ( struct net_device * netdev , struct ethtool_cmd * ecmd )
{
struct ucc_geth_private * ugeth = netdev_priv ( netdev ) ;
struct phy_device * phydev = ugeth - > phydev ;
if ( ! phydev )
return - ENODEV ;
return phy_ethtool_sset ( phydev , ecmd ) ;
}
static void
uec_get_pauseparam ( struct net_device * netdev ,
struct ethtool_pauseparam * pause )
{
struct ucc_geth_private * ugeth = netdev_priv ( netdev ) ;
pause - > autoneg = ugeth - > phydev - > autoneg ;
if ( ugeth - > ug_info - > receiveFlowControl )
pause - > rx_pause = 1 ;
if ( ugeth - > ug_info - > transmitFlowControl )
pause - > tx_pause = 1 ;
}
static int
uec_set_pauseparam ( struct net_device * netdev ,
struct ethtool_pauseparam * pause )
{
struct ucc_geth_private * ugeth = netdev_priv ( netdev ) ;
int ret = 0 ;
ugeth - > ug_info - > receiveFlowControl = pause - > rx_pause ;
ugeth - > ug_info - > transmitFlowControl = pause - > tx_pause ;
2008-05-13 09:41:58 +04:00
2007-07-19 07:47:47 +04:00
if ( ugeth - > phydev - > autoneg ) {
if ( netif_running ( netdev ) ) {
/* FIXME: automatically restart */
printk ( KERN_INFO
" Please re-open the interface. \n " ) ;
}
} else {
struct ucc_geth_info * ug_info = ugeth - > ug_info ;
ret = init_flow_control_params ( ug_info - > aufc ,
ug_info - > receiveFlowControl ,
ug_info - > transmitFlowControl ,
ug_info - > pausePeriod ,
ug_info - > extensionField ,
& ugeth - > uccf - > uf_regs - > upsmr ,
& ugeth - > ug_regs - > uempr ,
& ugeth - > ug_regs - > maccfg1 ) ;
}
return ret ;
}
static uint32_t
uec_get_msglevel ( struct net_device * netdev )
{
struct ucc_geth_private * ugeth = netdev_priv ( netdev ) ;
return ugeth - > msg_enable ;
}
static void
uec_set_msglevel ( struct net_device * netdev , uint32_t data )
{
struct ucc_geth_private * ugeth = netdev_priv ( netdev ) ;
ugeth - > msg_enable = data ;
}
static int
uec_get_regs_len ( struct net_device * netdev )
{
return sizeof ( struct ucc_geth ) ;
}
static void
uec_get_regs ( struct net_device * netdev ,
struct ethtool_regs * regs , void * p )
{
int i ;
struct ucc_geth_private * ugeth = netdev_priv ( netdev ) ;
u32 __iomem * ug_regs = ( u32 __iomem * ) ugeth - > ug_regs ;
u32 * buff = p ;
for ( i = 0 ; i < sizeof ( struct ucc_geth ) / sizeof ( u32 ) ; i + + )
buff [ i ] = in_be32 ( & ug_regs [ i ] ) ;
}
static void
uec_get_ringparam ( struct net_device * netdev ,
struct ethtool_ringparam * ring )
{
struct ucc_geth_private * ugeth = netdev_priv ( netdev ) ;
struct ucc_geth_info * ug_info = ugeth - > ug_info ;
int queue = 0 ;
ring - > rx_max_pending = UCC_GETH_BD_RING_SIZE_MAX ;
ring - > rx_mini_max_pending = UCC_GETH_BD_RING_SIZE_MAX ;
ring - > rx_jumbo_max_pending = UCC_GETH_BD_RING_SIZE_MAX ;
ring - > tx_max_pending = UCC_GETH_BD_RING_SIZE_MAX ;
ring - > rx_pending = ug_info - > bdRingLenRx [ queue ] ;
ring - > rx_mini_pending = ug_info - > bdRingLenRx [ queue ] ;
ring - > rx_jumbo_pending = ug_info - > bdRingLenRx [ queue ] ;
ring - > tx_pending = ug_info - > bdRingLenTx [ queue ] ;
}
static int
uec_set_ringparam ( struct net_device * netdev ,
struct ethtool_ringparam * ring )
{
struct ucc_geth_private * ugeth = netdev_priv ( netdev ) ;
struct ucc_geth_info * ug_info = ugeth - > ug_info ;
int queue = 0 , ret = 0 ;
if ( ring - > rx_pending < UCC_GETH_RX_BD_RING_SIZE_MIN ) {
printk ( " %s: RxBD ring size must be no smaller than %d. \n " ,
netdev - > name , UCC_GETH_RX_BD_RING_SIZE_MIN ) ;
return - EINVAL ;
}
if ( ring - > rx_pending % UCC_GETH_RX_BD_RING_SIZE_ALIGNMENT ) {
printk ( " %s: RxBD ring size must be multiple of %d. \n " ,
netdev - > name , UCC_GETH_RX_BD_RING_SIZE_ALIGNMENT ) ;
return - EINVAL ;
}
if ( ring - > tx_pending < UCC_GETH_TX_BD_RING_SIZE_MIN ) {
printk ( " %s: TxBD ring size must be no smaller than %d. \n " ,
netdev - > name , UCC_GETH_TX_BD_RING_SIZE_MIN ) ;
return - EINVAL ;
}
ug_info - > bdRingLenRx [ queue ] = ring - > rx_pending ;
ug_info - > bdRingLenTx [ queue ] = ring - > tx_pending ;
if ( netif_running ( netdev ) ) {
/* FIXME: restart automatically */
printk ( KERN_INFO
" Please re-open the interface. \n " ) ;
}
return ret ;
}
2007-10-04 05:07:32 +04:00
static int uec_get_sset_count ( struct net_device * netdev , int sset )
2007-07-19 07:47:47 +04:00
{
struct ucc_geth_private * ugeth = netdev_priv ( netdev ) ;
u32 stats_mode = ugeth - > ug_info - > statisticsMode ;
int len = 0 ;
2007-10-04 05:07:32 +04:00
switch ( sset ) {
case ETH_SS_STATS :
if ( stats_mode & UCC_GETH_STATISTICS_GATHERING_MODE_HARDWARE )
len + = UEC_HW_STATS_LEN ;
if ( stats_mode & UCC_GETH_STATISTICS_GATHERING_MODE_FIRMWARE_TX )
len + = UEC_TX_FW_STATS_LEN ;
if ( stats_mode & UCC_GETH_STATISTICS_GATHERING_MODE_FIRMWARE_RX )
len + = UEC_RX_FW_STATS_LEN ;
return len ;
2007-07-19 07:47:47 +04:00
2007-10-04 05:07:32 +04:00
default :
return - EOPNOTSUPP ;
}
2007-07-19 07:47:47 +04:00
}
static void uec_get_strings ( struct net_device * netdev , u32 stringset , u8 * buf )
{
struct ucc_geth_private * ugeth = netdev_priv ( netdev ) ;
u32 stats_mode = ugeth - > ug_info - > statisticsMode ;
if ( stats_mode & UCC_GETH_STATISTICS_GATHERING_MODE_HARDWARE ) {
memcpy ( buf , hw_stat_gstrings , UEC_HW_STATS_LEN *
ETH_GSTRING_LEN ) ;
buf + = UEC_HW_STATS_LEN * ETH_GSTRING_LEN ;
}
if ( stats_mode & UCC_GETH_STATISTICS_GATHERING_MODE_FIRMWARE_TX ) {
memcpy ( buf , tx_fw_stat_gstrings , UEC_TX_FW_STATS_LEN *
ETH_GSTRING_LEN ) ;
buf + = UEC_TX_FW_STATS_LEN * ETH_GSTRING_LEN ;
}
if ( stats_mode & UCC_GETH_STATISTICS_GATHERING_MODE_FIRMWARE_RX )
2008-05-23 14:11:26 +04:00
memcpy ( buf , rx_fw_stat_gstrings , UEC_RX_FW_STATS_LEN *
2007-07-19 07:47:47 +04:00
ETH_GSTRING_LEN ) ;
}
static void uec_get_ethtool_stats ( struct net_device * netdev ,
struct ethtool_stats * stats , uint64_t * data )
{
struct ucc_geth_private * ugeth = netdev_priv ( netdev ) ;
u32 stats_mode = ugeth - > ug_info - > statisticsMode ;
u32 __iomem * base ;
int i , j = 0 ;
if ( stats_mode & UCC_GETH_STATISTICS_GATHERING_MODE_HARDWARE ) {
2009-08-27 11:35:47 +04:00
if ( ugeth - > ug_regs )
base = ( u32 __iomem * ) & ugeth - > ug_regs - > tx64 ;
else
base = NULL ;
2007-07-19 07:47:47 +04:00
for ( i = 0 ; i < UEC_HW_STATS_LEN ; i + + )
2009-08-27 11:35:47 +04:00
data [ j + + ] = base ? in_be32 ( & base [ i ] ) : 0 ;
2007-07-19 07:47:47 +04:00
}
if ( stats_mode & UCC_GETH_STATISTICS_GATHERING_MODE_FIRMWARE_TX ) {
base = ( u32 __iomem * ) ugeth - > p_tx_fw_statistics_pram ;
for ( i = 0 ; i < UEC_TX_FW_STATS_LEN ; i + + )
2008-11-13 21:26:27 +03:00
data [ j + + ] = base ? in_be32 ( & base [ i ] ) : 0 ;
2007-07-19 07:47:47 +04:00
}
if ( stats_mode & UCC_GETH_STATISTICS_GATHERING_MODE_FIRMWARE_RX ) {
base = ( u32 __iomem * ) ugeth - > p_rx_fw_statistics_pram ;
for ( i = 0 ; i < UEC_RX_FW_STATS_LEN ; i + + )
2008-11-13 21:26:27 +03:00
data [ j + + ] = base ? in_be32 ( & base [ i ] ) : 0 ;
2007-07-19 07:47:47 +04:00
}
}
static int uec_nway_reset ( struct net_device * netdev )
{
struct ucc_geth_private * ugeth = netdev_priv ( netdev ) ;
return phy_start_aneg ( ugeth - > phydev ) ;
}
/* Report driver information */
static void
uec_get_drvinfo ( struct net_device * netdev ,
struct ethtool_drvinfo * drvinfo )
{
strncpy ( drvinfo - > driver , DRV_NAME , 32 ) ;
strncpy ( drvinfo - > version , DRV_VERSION , 32 ) ;
strncpy ( drvinfo - > fw_version , " N/A " , 32 ) ;
strncpy ( drvinfo - > bus_info , " QUICC ENGINE " , 32 ) ;
drvinfo - > eedump_len = 0 ;
drvinfo - > regdump_len = uec_get_regs_len ( netdev ) ;
}
2009-08-27 11:35:57 +04:00
# ifdef CONFIG_PM
static void uec_get_wol ( struct net_device * netdev , struct ethtool_wolinfo * wol )
{
struct ucc_geth_private * ugeth = netdev_priv ( netdev ) ;
struct phy_device * phydev = ugeth - > phydev ;
if ( phydev & & phydev - > irq )
wol - > supported | = WAKE_PHY ;
if ( qe_alive_during_sleep ( ) )
wol - > supported | = WAKE_MAGIC ;
wol - > wolopts = ugeth - > wol_en ;
}
static int uec_set_wol ( struct net_device * netdev , struct ethtool_wolinfo * wol )
{
struct ucc_geth_private * ugeth = netdev_priv ( netdev ) ;
struct phy_device * phydev = ugeth - > phydev ;
if ( wol - > wolopts & ~ ( WAKE_PHY | WAKE_MAGIC ) )
return - EINVAL ;
else if ( wol - > wolopts & WAKE_PHY & & ( ! phydev | | ! phydev - > irq ) )
return - EINVAL ;
else if ( wol - > wolopts & WAKE_MAGIC & & ! qe_alive_during_sleep ( ) )
return - EINVAL ;
ugeth - > wol_en = wol - > wolopts ;
device_set_wakeup_enable ( & netdev - > dev , ugeth - > wol_en ) ;
return 0 ;
}
# else
# define uec_get_wol NULL
# define uec_set_wol NULL
# endif /* CONFIG_PM */
2007-07-19 07:47:47 +04:00
static const struct ethtool_ops uec_ethtool_ops = {
. get_settings = uec_get_settings ,
. set_settings = uec_set_settings ,
. get_drvinfo = uec_get_drvinfo ,
. get_regs_len = uec_get_regs_len ,
. get_regs = uec_get_regs ,
. get_msglevel = uec_get_msglevel ,
. set_msglevel = uec_set_msglevel ,
. nway_reset = uec_nway_reset ,
. get_link = ethtool_op_get_link ,
. get_ringparam = uec_get_ringparam ,
. set_ringparam = uec_set_ringparam ,
. get_pauseparam = uec_get_pauseparam ,
. set_pauseparam = uec_set_pauseparam ,
. set_sg = ethtool_op_set_sg ,
2007-10-04 05:07:32 +04:00
. get_sset_count = uec_get_sset_count ,
2007-07-19 07:47:47 +04:00
. get_strings = uec_get_strings ,
. get_ethtool_stats = uec_get_ethtool_stats ,
2009-08-27 11:35:57 +04:00
. get_wol = uec_get_wol ,
. set_wol = uec_set_wol ,
2007-07-19 07:47:47 +04:00
} ;
void uec_set_ethtool_ops ( struct net_device * netdev )
{
SET_ETHTOOL_OPS ( netdev , & uec_ethtool_ops ) ;
}