diff --git a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c index 0ce727e0..3d337282 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c +++ b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c @@ -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*/ diff --git a/drivers/net/ethernet/nvidia/nvethernet/ethtool.c b/drivers/net/ethernet/nvidia/nvethernet/ethtool.c index de4e35b0..3dc1303c 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/ethtool.c +++ b/drivers/net/ethernet/nvidia/nvethernet/ethtool.c @@ -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, }; /**