nvethernet: add pause frame support

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>
This commit is contained in:
Narayan Reddy
2019-05-04 19:22:13 +05:30
committed by Revanth Kumar Uppala
parent 731d296d1c
commit 0426fc74e2
2 changed files with 144 additions and 4 deletions

View File

@@ -40,6 +40,12 @@ static void ether_adjust_link(struct net_device *dev)
return;
}
if (phydev->link) {
if ((pdata->osi_core->pause_frames == OSI_PAUSE_FRAMES_ENABLE)
&& (phydev->pause || phydev->asym_pause)) {
osi_configure_flow_control(pdata->osi_core,
pdata->osi_core->flow_ctrl);
}
if (phydev->duplex != pdata->oldduplex) {
new_state = 1;
osi_set_mode(pdata->osi_core, phydev->duplex);
@@ -129,6 +135,16 @@ static int ether_phy_init(struct net_device *dev)
return -ENODEV;
}
/* In marvel phy driver pause is disabled. Instead of enabling
* in PHY driver, handle this in eqos driver so that enabling/disabling
* of the pause frame feature can be controlled based on the platform
*/
phydev->supported |= (SUPPORTED_Pause | SUPPORTED_Asym_Pause);
if (pdata->osi_core->pause_frames == OSI_PAUSE_FRAMES_DISABLE)
phydev->supported &= ~(SUPPORTED_Pause | SUPPORTED_Asym_Pause);
phydev->advertising = phydev->supported;
pdata->phydev = phydev;
return 0;
@@ -1995,8 +2011,8 @@ static void ether_parse_queue_prio(struct ether_priv_data *pdata,
ret = of_property_read_u32_array(pnode, pdt_prop, pval, num_entries);
if (ret < 0) {
dev_err(pdata->dev, "%s(): \"%s\" read failed %d. Using default\n",
__func__, pdt_prop, ret);
dev_err(pdata->dev, "%s(): \"%s\" read failed %d."
"Using default\n", __func__, pdt_prop, ret);
for (i = 0; i < num_entries; i++) {
pval[i] = val_def;
}
@@ -2008,8 +2024,8 @@ static void ether_parse_queue_prio(struct ether_priv_data *pdata,
*/
for (i = 0; i < num_entries; i++) {
if ((pval[i] > val_max) || ((pmask & (1U << pval[i])) != 0U)) {
dev_err(pdata->dev, "%s():Wrong or duplicate priority in DT entry for Q(%d)\n",
__func__, i);
dev_err(pdata->dev, "%s():Wrong or duplicate priority"
" in DT entry for Q(%d)\n", __func__, i);
pval[i] = val_def;
}
pmask |= 1U << pval[i];
@@ -2037,6 +2053,15 @@ static int ether_parse_dt(struct ether_priv_data *pdata)
struct platform_device *pdev = to_platform_device(dev);
int ret = -EINVAL;
/* Read Pause frame feature support */
ret = of_property_read_u32(np, "nvidia,pause_frames",
&pdata->osi_core->pause_frames);
if (ret < 0) {
dev_err(dev, "Failed to read nvida,pause_frames, so"
" setting to default support as enable\n");
pdata->osi_core->pause_frames = OSI_PAUSE_FRAMES_ENABLE;
}
/* Check if IOMMU is enabled */
if (pdev->dev.archdata.iommu != NULL) {
/* Read and set dma-mask from DT only if IOMMU is enabled*/

View File

@@ -16,10 +16,125 @@
#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,
};
/**