mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-24 02:01:36 +03:00
Pause frame is a flow control mechanism for temporarily stopping the transmission of data on Ethernet. The goal of this mechanism is to ensure zero packet loss in case of network congestion. Bug 200516459 Change-Id: I7b6373bfbb9572c4ac2635f1c4c91011f4244380 Signed-off-by: Narayan Reddy <narayanr@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/2111933 Reviewed-by: Bitan Biswas <bbiswas@nvidia.com> Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com> Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
155 lines
4.2 KiB
C
155 lines
4.2 KiB
C
/*
|
|
* Copyright (c) 2018-2019, NVIDIA CORPORATION. All rights reserved.
|
|
*
|
|
* 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, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "ether_linux.h"
|
|
|
|
/**
|
|
* ether_get_pauseparam - Get pause frame settings
|
|
* @ndev: network device instance
|
|
* @pause: Pause parameters that are set currently
|
|
*
|
|
* Algorithm: Gets pause frame configuration
|
|
*
|
|
* Dependencies: Network device needs to created.
|
|
*
|
|
* Protection: None.
|
|
*
|
|
* Return: None.
|
|
*/
|
|
static void ether_get_pauseparam(struct net_device *ndev,
|
|
struct ethtool_pauseparam *pause)
|
|
{
|
|
struct ether_priv_data *pdata = netdev_priv(ndev);
|
|
struct phy_device *phydev = pdata->phydev;
|
|
|
|
if (!netif_running(ndev)) {
|
|
netdev_err(pdata->ndev, "interface must be up\n");
|
|
return;
|
|
}
|
|
|
|
/* return if pause frame is not supported */
|
|
if ((pdata->osi_core->pause_frames == OSI_PAUSE_FRAMES_DISABLE) ||
|
|
(!(phydev->supported & SUPPORTED_Pause) ||
|
|
!(phydev->supported & SUPPORTED_Asym_Pause))) {
|
|
dev_err(pdata->dev, "FLOW control not supported\n");
|
|
return;
|
|
}
|
|
|
|
/* update auto negotiation */
|
|
pause->autoneg = phydev->autoneg;
|
|
|
|
/* update rx pause parameter */
|
|
if ((pdata->osi_core->flow_ctrl & OSI_FLOW_CTRL_RX) ==
|
|
OSI_FLOW_CTRL_RX) {
|
|
pause->rx_pause = 1;
|
|
}
|
|
|
|
/* update tx pause parameter */
|
|
if ((pdata->osi_core->flow_ctrl & OSI_FLOW_CTRL_TX) ==
|
|
OSI_FLOW_CTRL_TX) {
|
|
pause->tx_pause = 1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* ether_set_pauseparam - Set pause frame settings
|
|
* @ndev: network device instance
|
|
* @pause: Pause frame settings
|
|
*
|
|
* Algorithm: Sets pause frame settings
|
|
*
|
|
* Dependencies: Network device needs to created.
|
|
*
|
|
* Protection: None.
|
|
*
|
|
* Return: 0 - success, negative value - failure.
|
|
*/
|
|
static int ether_set_pauseparam(struct net_device *ndev,
|
|
struct ethtool_pauseparam *pause)
|
|
{
|
|
struct ether_priv_data *pdata = netdev_priv(ndev);
|
|
struct phy_device *phydev = pdata->phydev;
|
|
int curflow_ctrl = OSI_FLOW_CTRL_DISABLE;
|
|
int ret;
|
|
|
|
if (!netif_running(ndev)) {
|
|
netdev_err(pdata->ndev, "interface must be up\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* return if pause frame is not supported */
|
|
if ((pdata->osi_core->pause_frames == OSI_PAUSE_FRAMES_DISABLE) ||
|
|
(!(phydev->supported & SUPPORTED_Pause) ||
|
|
!(phydev->supported & SUPPORTED_Asym_Pause))) {
|
|
dev_err(pdata->dev, "FLOW control not supported\n");
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
dev_err(pdata->dev, "autoneg = %d tx_pause = %d rx_pause = %d\n",
|
|
pause->autoneg, pause->tx_pause, pause->rx_pause);
|
|
|
|
if (pause->tx_pause)
|
|
curflow_ctrl |= OSI_FLOW_CTRL_TX;
|
|
|
|
if (pause->rx_pause)
|
|
curflow_ctrl |= OSI_FLOW_CTRL_RX;
|
|
|
|
/* update flow control setting */
|
|
pdata->osi_core->flow_ctrl = curflow_ctrl;
|
|
/* update autonegotiation */
|
|
phydev->autoneg = pause->autoneg;
|
|
|
|
/*If autonegotiation is enabled,start auto-negotiation
|
|
* for this PHY device and return, so that flow control
|
|
* settings will be done once we receive the link changed
|
|
* event i.e in ether_adjust_link
|
|
*/
|
|
if (phydev->autoneg && netif_running(ndev)) {
|
|
return phy_start_aneg(phydev);
|
|
}
|
|
|
|
/* Configure current flow control settings */
|
|
ret = osi_configure_flow_control(pdata->osi_core,
|
|
pdata->osi_core->flow_ctrl);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static const struct ethtool_ops ether_ethtool_ops = {
|
|
.get_link = ethtool_op_get_link,
|
|
.get_link_ksettings = phy_ethtool_get_link_ksettings,
|
|
.set_link_ksettings = phy_ethtool_set_link_ksettings,
|
|
.get_pauseparam = ether_get_pauseparam,
|
|
.set_pauseparam = ether_set_pauseparam,
|
|
};
|
|
|
|
/**
|
|
* ether_set_ethtool_ops - Set ethtool operations
|
|
* @ndev: network device instance
|
|
*
|
|
* Algorithm: Sets ethtool operations in network
|
|
* device structure.
|
|
*
|
|
* Dependencies: Network device needs to created.
|
|
* Protection: None.
|
|
* Return: None.
|
|
*/
|
|
void ether_set_ethtool_ops(struct net_device *ndev)
|
|
{
|
|
ndev->ethtool_ops = ðer_ethtool_ops;
|
|
}
|