| /******************************************************************************* |
| 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; |
| } |
| } |