blob: 7732958b62723fd4d7656e1e257c7158f30dec16 [file] [log] [blame]
/*******************************************************************************
Copyright (C) Marvell International Ltd. and its affiliates
This software file (the "File") is owned and distributed by Marvell
International Ltd. and/or its affiliates ("Marvell") under the following
alternative licensing terms. Once you have made an election to distribute the
File under one of the following license alternatives, please (i) delete this
introductory statement regarding license alternatives, (ii) delete the two
license alternatives that you have not elected to use and (iii) preserve the
Marvell copyright notice above.
********************************************************************************
Marvell GPL License Option
If you received this File from Marvell, you may opt to use, redistribute and/or
modify this File in accordance with the terms and conditions of the General
Public License Version 2, June 1991 (the "GPL License"), a copy of which is
available along with the File in the license.txt file or by writing to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or
on the worldwide web at http://www.gnu.org/licenses/gpl.txt.
THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED
WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY
DISCLAIMED. The GPL License provides additional details about this warranty
disclaimer.
*******************************************************************************/
#include "mvCommon.h" /* Should be included before mvSysHwConfig */
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/pci.h>
#include <linux/ip.h>
#include <linux/in.h>
#include <linux/tcp.h>
#include <linux/string.h>
#include <net/ip.h>
#include <net/xfrm.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
#include <asm/arch/system.h>
#else
#include <mach/system.h>
#endif
#include "mvOs.h"
#include "dbg-trace.h"
#include "mvSysHwConfig.h"
#include "eth/mvEth.h"
#include "eth-phy/mvEthPhy.h"
#include "mvSysEthApi.h"
#include "ctrlEnv/mvCtrlEnvLib.h"
#include "mv_netdev.h"
#include "mv_eth_tool.h"
extern spinlock_t mii_lock;
const struct ethtool_ops mv_eth_tool_ops = {
.get_settings = mv_eth_tool_get_settings,
.set_settings = mv_eth_tool_set_settings,
.get_drvinfo = mv_eth_tool_get_drvinfo,
.get_regs_len = mv_eth_tool_get_regs_len,
.get_regs = mv_eth_tool_get_regs,
.nway_reset = mv_eth_tool_nway_reset,
.get_link = mv_eth_tool_get_link,
.get_coalesce = mv_eth_tool_get_coalesce,
.set_coalesce = mv_eth_tool_set_coalesce,
.get_ringparam = mv_eth_tool_get_ringparam,
.get_pauseparam = mv_eth_tool_get_pauseparam,
.set_pauseparam = mv_eth_tool_set_pauseparam,
.get_rx_csum = mv_eth_tool_get_rx_csum,
.set_rx_csum = mv_eth_tool_set_rx_csum,
.get_tx_csum = ethtool_op_get_tx_csum,
.set_tx_csum = mv_eth_tool_set_tx_csum,
.get_sg = ethtool_op_get_sg,
.set_sg = ethtool_op_set_sg,
.get_tso = ethtool_op_get_tso,
.set_tso = mv_eth_tool_set_tso,
.get_ufo = ethtool_op_get_ufo,
.set_ufo = mv_eth_tool_set_ufo,
.get_strings = mv_eth_tool_get_strings,
.phys_id = mv_eth_tool_phys_id,
.get_stats_count = mv_eth_tool_get_stats_count,
.get_ethtool_stats = mv_eth_tool_get_ethtool_stats,
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
.get_perm_addr = ethtool_op_get_perm_addr,
#endif
};
struct mv_eth_tool_stats {
char stat_string[ETH_GSTRING_LEN];
int stat_offset;
};
#define MV_ETH_TOOL_STAT(m) offsetof(mv_eth_priv, m)
static const struct mv_eth_tool_stats mv_eth_tool_global_strings_stats[] = {
{"skb_alloc_fail", MV_ETH_TOOL_STAT(eth_stat.skb_alloc_fail)},
{"tx_timeout", MV_ETH_TOOL_STAT(eth_stat.tx_timeout)},
{"tx_netif_stop", MV_ETH_TOOL_STAT(eth_stat.tx_netif_stop)},
{"tx_done_netif_wake", MV_ETH_TOOL_STAT(eth_stat.tx_done_netif_wake)},
{"tx_skb_no_headroom", MV_ETH_TOOL_STAT(eth_stat.tx_skb_no_headroom)},
#ifdef CONFIG_MV_ETH_STATS_INFO
{"irq_total", MV_ETH_TOOL_STAT(eth_stat.irq_total)},
{"irq_none_events", MV_ETH_TOOL_STAT(eth_stat.irq_none)},
{"irq_while_polling", MV_ETH_TOOL_STAT(eth_stat.irq_while_polling)},
{"picr is", MV_ETH_TOOL_STAT(picr)},
{"picer is", MV_ETH_TOOL_STAT(picer)},
{"poll_events", MV_ETH_TOOL_STAT(eth_stat.poll_events)},
{"poll_complete", MV_ETH_TOOL_STAT(eth_stat.poll_complete)},
{"tx_events", MV_ETH_TOOL_STAT(eth_stat.tx_events)},
{"tx_done_events", MV_ETH_TOOL_STAT(eth_stat.tx_done_events)},
{"timer_events", MV_ETH_TOOL_STAT(eth_stat.timer_events)},
#if defined(CONFIG_MV_ETH_NFP) || defined(CONFIG_MV_SKB_REUSE)
{"rx_pool_empty", MV_ETH_TOOL_STAT(eth_stat.rx_pool_empty)},
#endif /* CONFIG_MV_ETH_NFP || CONFIG_MV_SKB_REUSE */
#endif /* CONFIG_MV_ETH_STATS_INFO */
#ifdef CONFIG_MV_ETH_STATS_DEBUG
{"skb_alloc_ok", MV_ETH_TOOL_STAT(eth_stat.skb_alloc_ok)},
{"skb_free_ok", MV_ETH_TOOL_STAT(eth_stat.skb_free_ok)},
#ifdef CONFIG_MV_SKB_REUSE
{"skb_reuse_rx", MV_ETH_TOOL_STAT(eth_stat.skb_reuse_rx)},
{"skb_reuse_tx", MV_ETH_TOOL_STAT(eth_stat.skb_reuse_tx)},
{"skb_reuse_alloc", MV_ETH_TOOL_STAT(eth_stat.skb_reuse_alloc)},
#endif /* CONFIG_MV_SKB_REUSE */
{"tx_csum_hw", MV_ETH_TOOL_STAT(eth_stat.tx_csum_hw)},
{"tx_csum_sw", MV_ETH_TOOL_STAT(eth_stat.tx_csum_sw)},
{"rx_netif_drop", MV_ETH_TOOL_STAT(eth_stat.rx_netif_drop)},
{"rx_csum_hw", MV_ETH_TOOL_STAT(eth_stat.rx_csum_hw)},
{"rx_csum_hw_frags", MV_ETH_TOOL_STAT(eth_stat.rx_csum_hw_frags)},
{"rx_csum_sw", MV_ETH_TOOL_STAT(eth_stat.rx_csum_sw)},
#ifdef ETH_INCLUDE_LRO
{"rx_lro_aggregated", MV_ETH_TOOL_STAT(lro_mgr.stats.aggregated)},
{"rx_lro_flushed", MV_ETH_TOOL_STAT(lro_mgr.stats.flushed)},
{"rx_lro_defragmented", MV_ETH_TOOL_STAT(lro_mgr.stats.defragmented)},
{"rx_lro_no_resources", MV_ETH_TOOL_STAT(lro_mgr.stats.no_desc)},
#endif /* ETH_INCLUDE_LRO */
#ifdef ETH_MV_TX_EN
{"tx_en_done", MV_ETH_TOOL_STAT(eth_stat.tx_en_done)},
{"tx_en_busy", MV_ETH_TOOL_STAT(eth_stat.tx_en_busy)},
{"tx_en_wait", MV_ETH_TOOL_STAT(eth_stat.tx_en_wait)},
{"tx_en_wait_count", MV_ETH_TOOL_STAT(eth_stat.tx_en_wait_count)},
#endif /* ETH_MV_TX_EN */
#endif /* CONFIG_MV_ETH_STATS_DEBUG */
};
static const struct mv_eth_tool_stats mv_eth_tool_rx_queue_strings_stats[] = {
#ifdef CONFIG_MV_ETH_STATS_DEBUG
{"rx_hal_ok", MV_ETH_TOOL_STAT(eth_stat.rx_hal_ok)},
{"rx_fill_ok", MV_ETH_TOOL_STAT(eth_stat.rx_fill_ok)},
#endif /* CONFIG_MV_ETH_STATS_DEBUG */
};
static const struct mv_eth_tool_stats mv_eth_tool_tx_queue_strings_stats[] = {
#ifdef CONFIG_MV_ETH_STATS_DEBUG
{"tx_count", MV_ETH_TOOL_STAT(tx_count)},
{"tx_hal_ok", MV_ETH_TOOL_STAT(eth_stat.tx_hal_ok)},
{"tx_hal_no_resource", MV_ETH_TOOL_STAT(eth_stat.tx_hal_no_resource)},
{"tx_done_hal_ok", MV_ETH_TOOL_STAT(eth_stat.tx_done_hal_ok)},
#endif /* CONFIG_MV_ETH_STATS_DEBUG */
};
#define MV_ETH_TOOL_RX_QUEUE_STATS_LEN \
sizeof(mv_eth_tool_rx_queue_strings_stats) / sizeof(struct mv_eth_tool_stats)
#define MV_ETH_TOOL_TX_QUEUE_STATS_LEN \
sizeof(mv_eth_tool_tx_queue_strings_stats) / sizeof(struct mv_eth_tool_stats)
#define MV_ETH_TOOL_QUEUE_STATS_LEN \
((MV_ETH_TOOL_RX_QUEUE_STATS_LEN * MV_ETH_RX_Q_NUM) + \
(MV_ETH_TOOL_TX_QUEUE_STATS_LEN * MV_ETH_TX_Q_NUM))
#define MV_ETH_TOOL_GLOBAL_STATS_LEN \
sizeof(mv_eth_tool_global_strings_stats) / sizeof(struct mv_eth_tool_stats)
#define MV_ETH_TOOL_STATS_LEN \
(MV_ETH_TOOL_GLOBAL_STATS_LEN + MV_ETH_TOOL_QUEUE_STATS_LEN)
/******************************************************************************
* mv_eth_tool_read_mdio
* Description:
* MDIO read implementation for kernel core MII calls
* INPUT:
* netdev Network device structure pointer
* addr PHY address
* reg PHY register number (offset)
* OUTPUT
* Register value or -1 on error
*
*******************************************************************************/
int mv_eth_tool_read_mdio(struct net_device *netdev, int addr, int reg)
{
unsigned long flags;
unsigned short value;
MV_STATUS status;
spin_lock_irqsave(&mii_lock, flags);
status = mvEthPhyRegRead(addr, reg, &value);
spin_unlock_irqrestore(&mii_lock, flags);
if (status == MV_OK)
return value;
return -1;
}
/******************************************************************************
* mv_eth_tool_write_mdio
* Description:
* MDIO write implementation for kernel core MII calls
* INPUT:
* netdev Network device structure pointer
* addr PHY address
* reg PHY register number (offset)
* data Data to be written into PHY register
* OUTPUT
* None
*
*******************************************************************************/
void mv_eth_tool_write_mdio(struct net_device *netdev, int addr, int reg, int data)
{
unsigned long flags;
unsigned short tmp = (unsigned short)data;
spin_lock_irqsave(&mii_lock, flags);
mvEthPhyRegWrite(addr, reg, tmp);
spin_unlock_irqrestore(&mii_lock, flags);
}
/******************************************************************************
* mv_eth_tool_read_phy_reg
* Description:
* Marvell PHY register read (includes page number)
* INPUT:
* phy_addr PHY address
* page PHY register page (region)
* reg PHY register number (offset)
* OUTPUT
* val PHY register value
* RETURN:
* 0 for success
*
*******************************************************************************/
#define MV_ETH_TOOL_PHY_PAGE_ADDR_REG 22
int mv_eth_tool_read_phy_reg(int phy_addr, u16 page, u16 reg, u16 *val)
{
unsigned long flags;
MV_STATUS status = 0;
spin_lock_irqsave(&mii_lock, flags);
/* setup register address page first */
if (!mvEthPhyRegWrite(phy_addr, MV_ETH_TOOL_PHY_PAGE_ADDR_REG, page)) {
status = mvEthPhyRegRead(phy_addr, reg, val);
}
spin_unlock_irqrestore(&mii_lock, flags);
return status;
}
/******************************************************************************
* mv_eth_tool_write_phy_reg
* Description:
* Marvell PHY register write (includes page number)
* INPUT:
* phy_addr PHY address
* page PHY register page (region)
* reg PHY register number (offset)
* data Data to be written into PHY register
* OUTPUT
* None
* RETURN:
* 0 for success
*
*******************************************************************************/
int mv_eth_tool_write_phy_reg(int phy_addr, u16 page, u16 reg, u16 data)
{
unsigned long flags;
MV_STATUS status = 0;
spin_lock_irqsave(&mii_lock, flags);
/* setup register address page first */
if (!mvEthPhyRegWrite(phy_addr, MV_ETH_TOOL_PHY_PAGE_ADDR_REG,
(unsigned int)page)) {
status = mvEthPhyRegWrite(phy_addr, reg, data);
}
spin_unlock_irqrestore(&mii_lock, flags);
return status;
}
/******************************************************************************
* mv_eth_tool_get_settings
* Description:
* ethtool get standard port settings
* INPUT:
* netdev Network device structure pointer
* OUTPUT
* cmd command (settings)
* RETURN:
* 0 for success
*
*******************************************************************************/
int mv_eth_tool_get_settings(struct net_device *netdev, struct ethtool_cmd *cmd)
{
mv_eth_priv *priv = MV_ETH_PRIV(netdev);
struct mii_if_info mii;
int retval;
MV_ETH_PORT_STATUS status;
#ifdef CONFIG_MII
mii.dev = netdev;
mii.phy_id_mask = 0x1F;
mii.reg_num_mask = 0x1F;
mii.mdio_read = mv_eth_tool_read_mdio;
mii.mdio_write = mv_eth_tool_write_mdio;
mii.phy_id = priv->phy_id;
mii.supports_gmii = 1;
/* Get values from PHY */
retval = mii_ethtool_gset(&mii, cmd);
if (retval)
return retval;
#endif
/* Get some values from MAC */
mvEthStatusGet(priv->hal_priv, &status);
switch (status.speed) {
case MV_ETH_SPEED_1000:
cmd->speed = SPEED_1000;
break;
case MV_ETH_SPEED_100:
cmd->speed = SPEED_100;
break;
case MV_ETH_SPEED_10:
cmd->speed = SPEED_10;
break;
default:
return -EINVAL;
}
if (status.duplex == MV_ETH_DUPLEX_FULL)
cmd->duplex = 1;
else
cmd->duplex = 0;
cmd->port = PORT_MII;
cmd->phy_address = priv->phy_id;
return 0;
}
/******************************************************************************
* mv_eth_tool_restore_settings
* Description:
* restore saved speed/dublex/an settings
* INPUT:
* netdev Network device structure pointer
* OUTPUT
* None
* RETURN:
* 0 for success
*
*******************************************************************************/
int mv_eth_tool_restore_settings(struct net_device *netdev)
{
mv_eth_priv *priv = MV_ETH_PRIV(netdev);
int mv_phy_speed, mv_phy_duplex;
MV_U32 mv_phy_addr = priv->phy_id;
MV_ETH_PORT_SPEED mv_mac_speed;
MV_ETH_PORT_DUPLEX mv_mac_duplex;
int err = -EINVAL;
switch (priv->speed_cfg) {
case SPEED_10:
mv_phy_speed = 0;
mv_mac_speed = MV_ETH_SPEED_10;
break;
case SPEED_100:
mv_phy_speed = 1;
mv_mac_speed = MV_ETH_SPEED_100;
break;
case SPEED_1000:
mv_phy_speed = 2;
mv_mac_speed = MV_ETH_SPEED_1000;
break;
default:
return -EINVAL;
}
switch (priv->duplex_cfg) {
case DUPLEX_HALF:
mv_phy_duplex = 0;
mv_mac_duplex = MV_ETH_DUPLEX_HALF;
break;
case DUPLEX_FULL:
mv_phy_duplex = 1;
mv_mac_duplex = MV_ETH_DUPLEX_FULL;
break;
default:
return -EINVAL;
}
if (priv->autoneg_cfg == AUTONEG_ENABLE) {
err = mvEthSpeedDuplexSet(priv->hal_priv,
MV_ETH_SPEED_AN, MV_ETH_DUPLEX_AN);
/* Restart AN on PHY enables it */
if (!err) {
err = mvEthPhyRestartAN(mv_phy_addr, MV_ETH_TOOL_AN_TIMEOUT);
if (err == MV_TIMEOUT) {
MV_ETH_PORT_STATUS ps;
mvEthStatusGet(priv->hal_priv, &ps);
if (!ps.isLinkUp)
err = 0;
}
}
} else if (priv->autoneg_cfg == AUTONEG_DISABLE) {
err = mvEthPhyDisableAN(mv_phy_addr, mv_phy_speed, mv_phy_duplex);
if (!err) {
err = mvEthSpeedDuplexSet(priv->hal_priv,
mv_mac_speed, mv_mac_duplex);
}
} else {
err = -EINVAL;
}
return err;
}
/******************************************************************************
* mv_eth_tool_set_settings
* Description:
* ethtool set standard port settings
* INPUT:
* netdev Network device structure pointer
* cmd command (settings)
* OUTPUT
* None
* RETURN:
* 0 for success
*
*******************************************************************************/
int mv_eth_tool_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
mv_eth_priv *priv = MV_ETH_PRIV(dev);
int _speed, _duplex, _autoneg, err;
#ifdef CONFIG_MV_GATEWAY
if (priv->isGtw)
return -EPERM;
#endif /* CONFIG_MV_GATEWAY */
_duplex = priv->duplex_cfg;
_speed = priv->speed_cfg;
_autoneg = priv->autoneg_cfg;
priv->duplex_cfg = cmd->duplex;
priv->speed_cfg = cmd->speed;
priv->autoneg_cfg = cmd->autoneg;
err = mv_eth_tool_restore_settings(dev);
if (err) {
priv->duplex_cfg = _duplex;
priv->speed_cfg = _speed;
priv->autoneg_cfg = _autoneg;
}
return err;
}
/******************************************************************************
* mv_eth_tool_get_drvinfo
* Description:
* ethtool get driver information
* INPUT:
* netdev Network device structure pointer
* info driver information
* OUTPUT
* info driver information
* RETURN:
* None
*
*******************************************************************************/
void mv_eth_tool_get_drvinfo(struct net_device *netdev,
struct ethtool_drvinfo *info)
{
strcpy(info->driver, "mv_eth");
strcpy(info->version, LSP_VERSION);
strcpy(info->fw_version, "N/A");
strcpy(info->bus_info, "Mbus");
info->n_stats = MV_ETH_TOOL_STATS_LEN;
info->testinfo_len = 0;
info->regdump_len = mv_eth_tool_get_regs_len(netdev);
info->eedump_len = 0;
}
/******************************************************************************
* mv_eth_tool_get_regs_len
* Description:
* ethtool get registers array length
* INPUT:
* netdev Network device structure pointer
* OUTPUT
* None
* RETURN:
* registers array length
*
*******************************************************************************/
int mv_eth_tool_get_regs_len(struct net_device *netdev)
{
#define MV_ETH_TOOL_REGS_LEN 32
return MV_ETH_TOOL_REGS_LEN * sizeof(uint32_t);
}
/******************************************************************************
* mv_eth_tool_get_regs
* Description:
* ethtool get registers array
* INPUT:
* netdev Network device structure pointer
* regs registers information
* OUTPUT
* p registers array
* RETURN:
* None
*
*******************************************************************************/
void mv_eth_tool_get_regs(struct net_device *netdev,
struct ethtool_regs *regs, void *p)
{
mv_eth_priv *priv = MV_ETH_PRIV(netdev);
uint32_t *regs_buff = p;
memset(p, 0, MV_ETH_TOOL_REGS_LEN * sizeof(uint32_t));
regs->version = mvCtrlModelRevGet();
/* ETH port registers */
regs_buff[0] = MV_REG_READ(ETH_PORT_STATUS_REG(priv->port));
regs_buff[1] = MV_REG_READ(ETH_PORT_SERIAL_CTRL_REG(priv->port));
regs_buff[2] = MV_REG_READ(ETH_PORT_CONFIG_REG(priv->port));
regs_buff[3] = MV_REG_READ(ETH_PORT_CONFIG_EXTEND_REG(priv->port));
regs_buff[4] = MV_REG_READ(ETH_SDMA_CONFIG_REG(priv->port));
regs_buff[5] = MV_REG_READ(ETH_TX_FIFO_URGENT_THRESH_REG(priv->port));
regs_buff[6] = MV_REG_READ(ETH_RX_QUEUE_COMMAND_REG(priv->port));
regs_buff[7] = MV_REG_READ(ETH_TX_QUEUE_COMMAND_REG(priv->port));
regs_buff[8] = MV_REG_READ(ETH_INTR_CAUSE_REG(priv->port));
regs_buff[9] = MV_REG_READ(ETH_INTR_CAUSE_EXT_REG(priv->port));
regs_buff[10] = MV_REG_READ(ETH_INTR_MASK_REG(priv->port));
regs_buff[11] = MV_REG_READ(ETH_INTR_MASK_EXT_REG(priv->port));
regs_buff[12] = MV_REG_READ(ETH_RX_DESCR_STAT_CMD_REG(priv->port, 0));
regs_buff[13] = MV_REG_READ(ETH_RX_BYTE_COUNT_REG(priv->port, 0));
regs_buff[14] = MV_REG_READ(ETH_RX_BUF_PTR_REG(priv->port, 0));
regs_buff[15] = MV_REG_READ(ETH_RX_CUR_DESC_PTR_REG(priv->port, 0));
/* ETH Unit registers */
regs_buff[16] = MV_REG_READ(ETH_PHY_ADDR_REG(priv->port));
regs_buff[17] = MV_REG_READ(ETH_UNIT_INTR_CAUSE_REG(priv->port));
regs_buff[18] = MV_REG_READ(ETH_UNIT_INTR_MASK_REG(priv->port));
regs_buff[19] = MV_REG_READ(ETH_UNIT_ERROR_ADDR_REG(priv->port));
regs_buff[20] = MV_REG_READ(ETH_UNIT_INT_ADDR_ERROR_REG(priv->port));
}
/******************************************************************************
* mv_eth_tool_nway_reset
* Description:
* ethtool restart auto negotiation
* INPUT:
* netdev Network device structure pointer
* OUTPUT
* None
* RETURN:
* 0 on success
*
*******************************************************************************/
int mv_eth_tool_nway_reset(struct net_device *netdev)
{
mv_eth_priv *priv = MV_ETH_PRIV(netdev);
MV_U32 mv_phy_addr = (MV_U32)(priv->phy_id);
if (mvEthPhyRestartAN(mv_phy_addr, MV_ETH_TOOL_AN_TIMEOUT) != MV_OK)
return -EINVAL;
return 0;
}
/******************************************************************************
* mv_eth_tool_get_link
* Description:
* ethtool get link status
* INPUT:
* netdev Network device structure pointer
* OUTPUT
* None
* RETURN:
* 0 if link is down, 1 if link is up
*
*******************************************************************************/
u32 mv_eth_tool_get_link(struct net_device *netdev)
{
mv_eth_priv *priv = MV_ETH_PRIV(netdev);
MV_ETH_PORT_STATUS status;
mvEthStatusGet(priv->hal_priv, &status);
if (status.isLinkUp == MV_TRUE)
return 1;
return 0;
}
/******************************************************************************
* mv_eth_tool_get_coalesce
* Description:
* ethtool get RX/TX coalesce parameters
* INPUT:
* netdev Network device structure pointer
* OUTPUT
* cmd Coalesce parameters
* RETURN:
* 0 on success
*
*******************************************************************************/
int mv_eth_tool_get_coalesce(struct net_device *netdev,
struct ethtool_coalesce *cmd)
{
mv_eth_priv *priv = MV_ETH_PRIV(netdev);
if (mvEthCoalGet(priv->hal_priv, &cmd->rx_coalesce_usecs,
&cmd->tx_coalesce_usecs) != MV_OK)
return -EINVAL;
return 0;
}
/******************************************************************************
* mv_eth_tool_set_coalesce
* Description:
* ethtool set RX/TX coalesce parameters
* INPUT:
* netdev Network device structure pointer
* cmd Coalesce parameters
* OUTPUT
* None
* RETURN:
* 0 on success
*
*******************************************************************************/
int mv_eth_tool_set_coalesce(struct net_device *netdev,
struct ethtool_coalesce *cmd)
{
mv_eth_priv *priv = MV_ETH_PRIV(netdev);
if ((cmd->rx_coalesce_usecs == 0) ||
(cmd->tx_coalesce_usecs == 0)) {
/* coalesce usec=0 means that coalesce frames should be used,
* which is not permitted (unsupported) */
return -EPERM;
}
if ((cmd->rx_coalesce_usecs * 166 / 64 > 0x3FFF) ||
(cmd->tx_coalesce_usecs * 166 / 64 > 0x3FFF))
return -EINVAL;
/* Save values for mv_eth_start_internals() */
priv->rx_coal_usec = cmd->rx_coalesce_usecs;
priv->tx_coal_usec = cmd->tx_coalesce_usecs;
mvEthRxCoalSet (priv->hal_priv, cmd->rx_coalesce_usecs);
mvEthTxCoalSet (priv->hal_priv, cmd->tx_coalesce_usecs);
return 0;
}
/******************************************************************************
* mv_eth_tool_get_ringparam
* Description:
* ethtool get ring parameters
* INPUT:
* netdev Network device structure pointer
* OUTPUT
* ring Ring paranmeters
* RETURN:
* None
*
*******************************************************************************/
void mv_eth_tool_get_ringparam( struct net_device *netdev,
struct ethtool_ringparam *ring)
{
ring->rx_max_pending = 4096;
ring->tx_max_pending = 4096;
ring->rx_mini_max_pending = 0;
ring->rx_jumbo_max_pending = 0;
ring->rx_pending = CONFIG_MV_ETH_NUM_OF_RX_DESCR;
ring->tx_pending = CONFIG_MV_ETH_NUM_OF_TX_DESCR;
ring->rx_mini_pending = 0;
ring->rx_jumbo_pending = 0;
}
/******************************************************************************
* mv_eth_tool_get_pauseparam
* Description:
* ethtool get pause parameters
* INPUT:
* netdev Network device structure pointer
* OUTPUT
* pause Pause paranmeters
* RETURN:
* None
*
*******************************************************************************/
void mv_eth_tool_get_pauseparam(struct net_device *netdev,
struct ethtool_pauseparam *pause)
{
mv_eth_priv *priv = MV_ETH_PRIV(netdev);
ETH_PORT_CTRL *pPortCtrl = (ETH_PORT_CTRL*)(priv->hal_priv);
MV_U32 reg;
/* FIXME */
printk("%s not supported for new GMAC\n", __FUNCTION__);
#if 0
reg = MV_REG_READ(ETH_PORT_SERIAL_CTRL_REG(pPortCtrl->portNo));
pause->rx_pause = 0;
pause->tx_pause = 0;
if (reg & ETH_DISABLE_FC_AUTO_NEG_MASK) { /* autoneg disabled */
pause->autoneg = AUTONEG_DISABLE;
if (reg & ETH_SET_FLOW_CTRL_MASK)
{
pause->rx_pause = 1;
pause->tx_pause = 1;
}
} else { /* autoneg enabled */
pause->autoneg = AUTONEG_ENABLE;
if (reg & ETH_ADVERTISE_SYM_FC_MASK)
{
pause->rx_pause = 1;
pause->tx_pause = 1;
}
}
#endif
}
/******************************************************************************
* mv_eth_tool_set_pauseparam
* Description:
* ethtool configure pause parameters
* INPUT:
* netdev Network device structure pointer
* pause Pause paranmeters
* OUTPUT
* None
* RETURN:
* 0 on success
*
*******************************************************************************/
int mv_eth_tool_set_pauseparam( struct net_device *netdev,
struct ethtool_pauseparam *pause)
{
mv_eth_priv *priv = MV_ETH_PRIV(netdev);
ETH_PORT_CTRL *pPortCtrl = (ETH_PORT_CTRL*)(priv->hal_priv);
if (pause->rx_pause && pause->tx_pause) { /* Enable FC */
if (pause->autoneg) { /* autoneg enable */
return mvEthFlowCtrlSet(priv->hal_priv, MV_ETH_FC_AN_ADV_SYM);
} else { /* autoneg disable */
return mvEthFlowCtrlSet(priv->hal_priv, MV_ETH_FC_ENABLE);
}
} else if (!pause->rx_pause && !pause->tx_pause) { /* Disable FC */
if (pause->autoneg) { /* autoneg enable */
return mvEthFlowCtrlSet(priv->hal_priv, MV_ETH_FC_AN_ADV_DIS);
} else { /* autoneg disable */
return mvEthFlowCtrlSet(priv->hal_priv, MV_ETH_FC_DISABLE);
}
}
/* Only symmetric change for RX and TX flow control is allowed */
return -EINVAL;
}
/******************************************************************************
* mv_eth_tool_get_rx_csum
* Description:
* ethtool get RX checksum offloading status
* INPUT:
* netdev Network device structure pointer
* OUTPUT
* None
* RETURN:
* RX checksum
*
*******************************************************************************/
u32 mv_eth_tool_get_rx_csum(struct net_device *netdev)
{
mv_eth_priv *priv = MV_ETH_PRIV(netdev);
#ifdef RX_CSUM_OFFLOAD
return (priv->rx_csum_offload != 0);
#else
return 0;
#endif
}
/******************************************************************************
* mv_eth_tool_set_rx_csum
* Description:
* ethtool enable/disable RX checksum offloading
* INPUT:
* netdev Network device structure pointer
* data Command data
* OUTPUT
* None
* RETURN:
* 0 on success
*
*******************************************************************************/
int mv_eth_tool_set_rx_csum(struct net_device *netdev, uint32_t data)
{
mv_eth_priv *priv = MV_ETH_PRIV(netdev);
#ifdef RX_CSUM_OFFLOAD
priv->rx_csum_offload = data;
return 0;
#else
return data ? -EINVAL : 0;
#endif
}
/******************************************************************************
* mv_eth_tool_set_tx_csum
* Description:
* ethtool enable/disable TX checksum offloading
* INPUT:
* netdev Network device structure pointer
* data Command data
* OUTPUT
* None
* RETURN:
* 0 on success
*
*******************************************************************************/
int mv_eth_tool_set_tx_csum(struct net_device *netdev, uint32_t data)
{
#ifdef TX_CSUM_OFFLOAD
if (data) {
netdev->features |= NETIF_F_IP_CSUM;
} else {
netdev->features &= ~NETIF_F_IP_CSUM;
}
return 0;
#else
return data ? -EINVAL : 0;
#endif /* TX_CSUM_OFFLOAD */
}
/******************************************************************************
* mv_eth_tool_set_tso
* Description:
* ethtool enable/disable TCP segmentation offloading
* INPUT:
* netdev Network device structure pointer
* data Command data
* OUTPUT
* None
* RETURN:
* 0 on success
*
*******************************************************************************/
int mv_eth_tool_set_tso(struct net_device *netdev, uint32_t data)
{
#ifdef ETH_INCLUDE_TSO
if (data) {
netdev->features |= NETIF_F_TSO;
} else {
netdev->features &= ~NETIF_F_TSO;
}
return 0;
#else
return data ? -EINVAL : 0;
#endif /* ETH_INCLUDE_TSO */
}
/******************************************************************************
* mv_eth_tool_set_ufo
* Description:
* ethtool enable/disable UDP segmentation offloading
* INPUT:
* netdev Network device structure pointer
* data Command data
* OUTPUT
* None
* RETURN:
* 0 on success
*
*******************************************************************************/
int mv_eth_tool_set_ufo(struct net_device *netdev, uint32_t data)
{
#ifdef ETH_INCLUDE_UFO
if (data) {
netdev->features |= NETIF_F_UFO;
} else {
netdev->features &= ~NETIF_F_UFO;
}
return 0;
#else
return data ? -EINVAL : 0;
#endif /* ETH_INCLUDE_UFO */
}
/******************************************************************************
* mv_eth_tool_get_strings
* Description:
* ethtool get strings (used for statistics and self-test descriptions)
* INPUT:
* netdev Network device structure pointer
* stringset strings parameters
* OUTPUT
* data output data
* RETURN:
* None
*
*******************************************************************************/
void mv_eth_tool_get_strings(struct net_device *netdev,
uint32_t stringset, uint8_t *data)
{
uint8_t *p = data;
int i, q;
char qnum[8][4] = {" Q0"," Q1"," Q2"," Q3"," Q4"," Q5"," Q6"," Q7"};
switch (stringset) {
case ETH_SS_TEST:
/*
memcpy(data, *mv_eth_tool_gstrings_test,
MV_ETH_TOOL_TEST_LEN*ETH_GSTRING_LEN); */
break;
case ETH_SS_STATS:
for (i = 0; i < MV_ETH_TOOL_GLOBAL_STATS_LEN; i++) {
memcpy(p, mv_eth_tool_global_strings_stats[i].stat_string,
ETH_GSTRING_LEN);
p += ETH_GSTRING_LEN;
}
for (q = 0; q < MV_ETH_RX_Q_NUM; q++) {
for (i = 0; i < MV_ETH_TOOL_RX_QUEUE_STATS_LEN; i++) {
const char *str = mv_eth_tool_rx_queue_strings_stats[i].stat_string;
memcpy(p, str, ETH_GSTRING_LEN);
strcat(p, qnum[q]);
p += ETH_GSTRING_LEN;
}
}
for (q = 0; q < MV_ETH_TX_Q_NUM; q++) {
for (i = 0; i < MV_ETH_TOOL_TX_QUEUE_STATS_LEN; i++) {
const char *str = mv_eth_tool_tx_queue_strings_stats[i].stat_string;
memcpy(p, str, ETH_GSTRING_LEN);
strcat(p, qnum[q]);
p += ETH_GSTRING_LEN;
}
}
break;
}
}
#define ETH_TOOL_PHY_LED_CTRL_PAGE 3
#define ETH_TOOL_PHY_LED_CTRL_REG 16
/******************************************************************************
* mv_eth_tool_get_link
* Description:
* ethtool physically identify port by LED blinking
* INPUT:
* netdev Network device structure pointer
* data Number of secunds to blink the LED
* OUTPUT
* None
* RETURN:
* 0 on success
*
*******************************************************************************/
int mv_eth_tool_phys_id(struct net_device *netdev, u32 data)
{
mv_eth_priv *priv = MV_ETH_PRIV(netdev);
u16 old_led_state;
if(!data || data > (u32)(MAX_SCHEDULE_TIMEOUT / HZ))
data = (u32)(MAX_SCHEDULE_TIMEOUT / HZ);
mv_eth_tool_read_phy_reg(priv->phy_id, ETH_TOOL_PHY_LED_CTRL_PAGE,
ETH_TOOL_PHY_LED_CTRL_REG, &old_led_state);
/* Forse LED blinking (all LED pins) */
mv_eth_tool_write_phy_reg(priv->phy_id, ETH_TOOL_PHY_LED_CTRL_PAGE,
ETH_TOOL_PHY_LED_CTRL_REG, 0x0BBB);
msleep_interruptible(data * 1000);
mv_eth_tool_write_phy_reg(priv->phy_id, ETH_TOOL_PHY_LED_CTRL_PAGE,
ETH_TOOL_PHY_LED_CTRL_REG, old_led_state);
return 0;
}
/******************************************************************************
* mv_eth_tool_get_stats_count
* Description:
* ethtool get statistics count (number of stat. array entries)
* INPUT:
* netdev Network device structure pointer
* OUTPUT
* None
* RETURN:
* statistics count
*
*******************************************************************************/
int mv_eth_tool_get_stats_count(struct net_device *netdev)
{
return MV_ETH_TOOL_STATS_LEN;
}
/******************************************************************************
* mv_eth_tool_get_ethtool_stats
* Description:
* ethtool get statistics
* INPUT:
* netdev Network device structure pointer
* stats stats parameters
* OUTPUT
* data output data
* RETURN:
* None
*
*******************************************************************************/
void mv_eth_tool_get_ethtool_stats(struct net_device *netdev,
struct ethtool_stats *stats, uint64_t *data)
{
mv_eth_priv *priv = MV_ETH_PRIV(netdev);
uint64_t *pdest = data;
int i, q;
for (i = 0; i < MV_ETH_TOOL_GLOBAL_STATS_LEN; i++) {
char *p = (char *)priv +
mv_eth_tool_global_strings_stats[i].stat_offset;
pdest[i] = *(uint32_t *)p;
}
pdest += MV_ETH_TOOL_GLOBAL_STATS_LEN;
for (q = 0; q < MV_ETH_RX_Q_NUM; q++) {
for (i = 0; i < MV_ETH_TOOL_RX_QUEUE_STATS_LEN; i++) {
char *p = (char *)priv +
mv_eth_tool_rx_queue_strings_stats[i].stat_offset;
pdest[i] = *((uint32_t *)p + q);
}
pdest += MV_ETH_TOOL_RX_QUEUE_STATS_LEN;
}
for (q = 0; q < MV_ETH_TX_Q_NUM; q++) {
for (i = 0; i < MV_ETH_TOOL_TX_QUEUE_STATS_LEN; i++) {
char *p = (char *)priv +
mv_eth_tool_tx_queue_strings_stats[i].stat_offset;
pdest[i] = *((uint32_t *)p + q);
}
pdest += MV_ETH_TOOL_TX_QUEUE_STATS_LEN;
}
}