nvethernet: Add support for EEE LPI

1. Add statistics for keeping track of EEE LPI entry/exit.
Invoke the API to configure EEE LPI in HW based on the PHY link
status and EEE support in PHY.

2. Add support to enable/disable EEE from ethtool.

Bug 2594864

Change-Id: Ib7703e087eb83269a351689f8d406564f799709a
Signed-off-by: Srinivas Ramachandran <srinivasra@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/2160786
Reviewed-by: Ajay Gupta <ajayg@nvidia.com>
GVS: Gerrit_Virtual_Submit
Reviewed-by: Mohan Thadikamalla <mohant@nvidia.com>
Reviewed-by: Bhadram Varka <vbhadram@nvidia.com>
Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
This commit is contained in:
Srinivas Ramachandran
2019-07-24 17:46:43 -07:00
committed by Revanth Kumar Uppala
parent 610e2b6a21
commit 7fb9f30e28
3 changed files with 311 additions and 0 deletions

View File

@@ -198,6 +198,61 @@ err_axi_cbb:
return ret; return ret;
} }
/**
* @brief ether_conf_eee - Init and configure EEE LPI in the MAC
*
* Algorithm:
* 1) Check if EEE is requested to be enabled/disabled.
* 2) If enabled, then check if current PHY speed/mode supports EEE.
* 3) If PHY supports, then enable the Tx LPI timers in MAC and set EEE active.
* 4) Else disable the Tx LPI timers in MAC and set EEE inactive.
*
* @param[in] pdata: Pointer to driver private data structure
* @param[in] tx_lpi_enable: Flag to indicate Tx LPI enable (1) or disable (0)
*
* @note MAC and PHY need to be initialized.
*
* @retval OSI_ENABLE if EEE is enabled/active
* @retval OSI_DISABLE if EEE is disabled/inactive
*/
int ether_conf_eee(struct ether_priv_data *pdata, unsigned int tx_lpi_enable)
{
int ret = 0;
struct phy_device *phydev = pdata->phydev;
unsigned int enable = tx_lpi_enable;
if (tx_lpi_enable) {
/* phy_init_eee() returns 0 if EEE is supported by the PHY */
if (phy_init_eee(phydev, OSI_ENABLE)) {
/* PHY does not support EEE, disable it in MAC */
enable = OSI_DISABLE;
} else {
/* PHY supports EEE, if link is up enable EEE */
enable = (unsigned int)phydev->link;
}
}
/* Enable EEE */
ret = osi_configure_eee(pdata->osi_core,
enable,
pdata->tx_lpi_timer);
/* Return current status of EEE based on OSI API success/failure */
if (ret != 0) {
if (enable) {
dev_warn(pdata->dev, "Failed to enable EEE\n");
ret = OSI_DISABLE;
} else {
dev_warn(pdata->dev, "Failed to disable EEE\n");
ret = OSI_ENABLE;
}
} else {
/* EEE enabled/disabled successfully as per enable flag */
ret = enable;
}
return ret;
}
/** /**
* @brief Adjust link call back * @brief Adjust link call back
* *
@@ -215,6 +270,7 @@ static void ether_adjust_link(struct net_device *dev)
struct phy_device *phydev = pdata->phydev; struct phy_device *phydev = pdata->phydev;
int new_state = 0, speed_changed = 0; int new_state = 0, speed_changed = 0;
unsigned long val; unsigned long val;
unsigned int eee_enable = OSI_DISABLE;
if (phydev == NULL) { if (phydev == NULL) {
return; return;
@@ -273,6 +329,13 @@ static void ether_adjust_link(struct net_device *dev)
"failed to do pad caliberation\n"); "failed to do pad caliberation\n");
} }
} }
/* Configure EEE if it is enabled */
if (pdata->eee_enabled && pdata->tx_lpi_enabled) {
eee_enable = OSI_ENABLE;
}
pdata->eee_active = ether_conf_eee(pdata, eee_enable);
} }
/** /**
@@ -1157,6 +1220,27 @@ static int ether_therm_init(struct ether_priv_data *pdata)
} }
#endif /* THERMAL_CAL */ #endif /* THERMAL_CAL */
/**
* @brief Initialize default EEE LPI configurations
*
* Algorithm: Update the defalt configuration/timers for EEE LPI in driver
* private data structure
*
* @param[in] pdata: OSD private data.
*/
static inline void ether_init_eee_params(struct ether_priv_data *pdata)
{
if (pdata->hw_feat.eee_sel) {
pdata->eee_enabled = OSI_ENABLE;
} else {
pdata->eee_enabled = OSI_DISABLE;
}
pdata->tx_lpi_enabled = pdata->eee_enabled;
pdata->eee_active = OSI_DISABLE;
pdata->tx_lpi_timer = OSI_DEFAULT_TX_LPI_TIMER;
}
/** /**
* @brief Call back to handle bring up of Ethernet interface * @brief Call back to handle bring up of Ethernet interface
* *
@@ -1280,6 +1364,9 @@ static int ether_open(struct net_device *dev)
goto err_r_irq; goto err_r_irq;
} }
/* Init EEE configuration */
ether_init_eee_params(pdata);
/* Start the MAC */ /* Start the MAC */
osi_start_mac(pdata->osi_core); osi_start_mac(pdata->osi_core);

View File

@@ -308,6 +308,14 @@ struct ether_priv_data {
bool clks_enable; bool clks_enable;
/** Delayed work queue to read RMON counters periodically */ /** Delayed work queue to read RMON counters periodically */
struct delayed_work ether_stats_work; struct delayed_work ether_stats_work;
/** Flag to check if EEE LPI is enabled for the MAC */
unsigned int eee_enabled;
/** Flag to check if EEE LPI is active currently */
unsigned int eee_active;
/** Flag to check if EEE LPI is enabled for MAC transmitter */
unsigned int tx_lpi_enabled;
/** Time (usec) MAC waits to enter LPI after Tx complete */
unsigned int tx_lpi_timer;
}; };
/** /**
@@ -381,4 +389,5 @@ int ether_handle_hwtstamp_ioctl(struct ether_priv_data *pdata,
struct ifreq *ifr); struct ifreq *ifr);
int ether_handle_priv_ts_ioctl(struct ether_priv_data *pdata, int ether_handle_priv_ts_ioctl(struct ether_priv_data *pdata,
struct ifreq *ifr); struct ifreq *ifr);
int ether_conf_eee(struct ether_priv_data *pdata, unsigned int tx_lpi_enable);
#endif /* ETHER_LINUX_H */ #endif /* ETHER_LINUX_H */

View File

@@ -197,6 +197,14 @@ static const struct eqos_stats eqos_mmc[] = {
EQOS_MMC_STAT(mmc_rx_fifo_overflow), EQOS_MMC_STAT(mmc_rx_fifo_overflow),
EQOS_MMC_STAT(mmc_rx_vlan_frames_gb), EQOS_MMC_STAT(mmc_rx_vlan_frames_gb),
EQOS_MMC_STAT(mmc_rx_watchdog_error), EQOS_MMC_STAT(mmc_rx_watchdog_error),
EQOS_MMC_STAT(mmc_rx_receive_error),
EQOS_MMC_STAT(mmc_rx_ctrl_frames_g),
/* LPI */
EQOS_MMC_STAT(mmc_tx_lpi_usec_cntr),
EQOS_MMC_STAT(mmc_tx_lpi_tran_cntr),
EQOS_MMC_STAT(mmc_rx_lpi_usec_cntr),
EQOS_MMC_STAT(mmc_rx_lpi_tran_cntr),
/* IPv4 */ /* IPv4 */
EQOS_MMC_STAT(mmc_rx_ipv4_gd), EQOS_MMC_STAT(mmc_rx_ipv4_gd),
@@ -700,6 +708,211 @@ static int ether_get_coalesce(struct net_device *dev,
return 0; return 0;
} }
/*
* @brief Get current EEE configuration in MAC/PHY
*
* Algorithm: This function is invoked by kernel when user request to get
* current EEE parameters. The function invokes the PHY framework to fill
* the supported & advertised EEE modes, as well as link partner EEE modes
* if it is available.
*
* @param[in] ndev: Net device data.
* @param[in] cur_eee: Pointer to struct ethtool_eee
*
* @note MAC and PHY need to be initialized.
*
* @retval 0 on Success.
* @retval -ve on Failure
*/
static int ether_get_eee(struct net_device *ndev,
struct ethtool_eee *cur_eee)
{
int ret;
struct ether_priv_data *pdata = netdev_priv(ndev);
struct phy_device *phydev = pdata->phydev;
if (!pdata->hw_feat.eee_sel) {
return -EOPNOTSUPP;
}
if (!netif_running(ndev)) {
netdev_err(pdata->ndev, "interface not up\n");
return -EINVAL;
}
ret = phy_ethtool_get_eee(phydev, cur_eee);
if (ret) {
netdev_warn(pdata->ndev, "Cannot get PHY EEE config\n");
return ret;
}
cur_eee->eee_enabled = pdata->eee_enabled;
cur_eee->tx_lpi_enabled = pdata->tx_lpi_enabled;
cur_eee->eee_active = pdata->eee_active;
cur_eee->tx_lpi_timer = pdata->tx_lpi_timer;
return ret;
}
/**
* @brief Helper routing to validate EEE configuration requested via ethtool
*
* Algorithm: Check for invalid combinations of ethtool_eee fields. If any
* invalid combination found, override it.
*
* @param[in] ndev: Net device data.
* @param[in] eee_req: Pointer to struct ethtool_eee configuration requested
*
* @retval none
*/
static inline void validate_eee_conf(struct net_device *ndev,
struct ethtool_eee *eee_req,
struct ethtool_eee cur_eee)
{
/* These are the invalid combinations that can be requested.
* EEE | Tx LPI | Rx LPI
*----------------------
* 0 | 0 | 1
* 0 | 1 | 0
* 0 | 1 | 1
* 1 | 0 | 0
*
* These combinations can be entered from a state where either EEE was
* enabled or disabled originally. Hence decide next valid state based
* on whether EEE has toggled or not.
*/
if (!eee_req->eee_enabled && !eee_req->tx_lpi_enabled &&
eee_req->advertised) {
if (eee_req->eee_enabled != cur_eee.eee_enabled) {
netdev_warn(ndev, "EEE off. Set Rx LPI off\n");
eee_req->advertised = OSI_DISABLE;
} else {
netdev_warn(ndev, "Rx LPI on. Set EEE on\n");
eee_req->eee_enabled = OSI_ENABLE;
}
}
if (!eee_req->eee_enabled && eee_req->tx_lpi_enabled &&
!eee_req->advertised) {
if (eee_req->eee_enabled != cur_eee.eee_enabled) {
netdev_warn(ndev, "EEE off. Set Tx LPI off\n");
eee_req->tx_lpi_enabled = OSI_DISABLE;
} else {
/* phy_init_eee will fail if Rx LPI advertisement is
* disabled. Hence change the adv back to enable,
* so that Tx LPI will be set.
*/
netdev_warn(ndev, "Tx LPI on. Set EEE & Rx LPI on\n");
eee_req->eee_enabled = OSI_ENABLE;
eee_req->advertised = eee_req->supported;
}
}
if (!eee_req->eee_enabled && eee_req->tx_lpi_enabled &&
eee_req->advertised) {
if (eee_req->eee_enabled != cur_eee.eee_enabled) {
netdev_warn(ndev, "EEE off. Set Tx & Rx LPI off\n");
eee_req->tx_lpi_enabled = OSI_DISABLE;
eee_req->advertised = OSI_DISABLE;
} else {
netdev_warn(ndev, "Tx & Rx LPI on. Set EEE on\n");
eee_req->eee_enabled = OSI_ENABLE;
}
}
if (eee_req->eee_enabled && !eee_req->tx_lpi_enabled &&
!eee_req->advertised) {
if (eee_req->eee_enabled != cur_eee.eee_enabled) {
netdev_warn(ndev, "EEE on. Set Tx & Rx LPI on\n");
eee_req->tx_lpi_enabled = OSI_ENABLE;
eee_req->advertised = eee_req->supported;
} else {
netdev_warn(ndev, "Tx,Rx LPI off. Set EEE off\n");
eee_req->eee_enabled = OSI_DISABLE;
}
}
}
/**
* @brief Set current EEE configuration
*
* Algorithm: This function is invoked by kernel when user request to Set
* current EEE parameters.
*
* @param[in] ndev: Net device data.
* @param[in] eee_req: Pointer to struct ethtool_eee
*
* @note MAC and PHY need to be initialized.
*
* @retval 0 on Success.
* @retval -ve on Failure
*/
static int ether_set_eee(struct net_device *ndev,
struct ethtool_eee *eee_req)
{
struct ether_priv_data *pdata = netdev_priv(ndev);
struct phy_device *phydev = pdata->phydev;
struct ethtool_eee cur_eee;
if (!pdata->hw_feat.eee_sel) {
return -EOPNOTSUPP;
}
if (!netif_running(ndev)) {
netdev_err(pdata->ndev, "interface not up\n");
return -EINVAL;
}
if (ether_get_eee(ndev, &cur_eee)) {
return -EOPNOTSUPP;
}
/* Validate args
* 1. Validate the tx lpi timer for acceptable range */
if (cur_eee.tx_lpi_timer != eee_req->tx_lpi_timer) {
if (eee_req->tx_lpi_timer == 0) {
pdata->tx_lpi_timer = OSI_DEFAULT_TX_LPI_TIMER;
} else if (eee_req->tx_lpi_timer <= OSI_MAX_TX_LPI_TIMER &&
eee_req->tx_lpi_timer >= OSI_MIN_TX_LPI_TIMER &&
!(eee_req->tx_lpi_timer % OSI_MIN_TX_LPI_TIMER)) {
pdata->tx_lpi_timer = eee_req->tx_lpi_timer;
} else {
netdev_err(ndev, "Tx LPI timer has to be < %u usec "
"in %u usec steps\n", OSI_MAX_TX_LPI_TIMER,
OSI_MIN_TX_LPI_TIMER);
return -EINVAL;
}
}
/* 2. Override invalid combinations of requested configuration */
validate_eee_conf(ndev, eee_req, cur_eee);
/* First store the requested & validated EEE configuration */
pdata->eee_enabled = eee_req->eee_enabled;
pdata->tx_lpi_enabled = eee_req->tx_lpi_enabled;
pdata->tx_lpi_timer = eee_req->tx_lpi_timer;
pdata->eee_active = eee_req->eee_active;
/* If EEE adv has changed, inform PHY framework. PHY will
* restart ANEG and the ether_adjust_link callback will take care of
* enabling Tx LPI as needed.
*/
if (cur_eee.advertised != eee_req->advertised) {
return phy_ethtool_set_eee(phydev, eee_req);
}
/* If no advertisement change, and only local Tx LPI changed, then
* configure the MAC right away.
*/
if (cur_eee.tx_lpi_enabled != eee_req->tx_lpi_enabled) {
eee_req->eee_active = ether_conf_eee(pdata,
eee_req->tx_lpi_enabled);
pdata->eee_active = eee_req->eee_active;
}
return 0;
}
/** /**
* @brief This function is invoked by kernel when user request to set * @brief This function is invoked by kernel when user request to set
* pmt parameters for remote wakeup or magic wakeup * pmt parameters for remote wakeup or magic wakeup
@@ -812,6 +1025,8 @@ static const struct ethtool_ops ether_ethtool_ops = {
.set_coalesce = ether_set_coalesce, .set_coalesce = ether_set_coalesce,
.get_wol = ether_get_wol, .get_wol = ether_get_wol,
.set_wol = ether_set_wol, .set_wol = ether_set_wol,
.get_eee = ether_get_eee,
.set_eee = ether_set_eee,
}; };
void ether_set_ethtool_ops(struct net_device *ndev) void ether_set_ethtool_ops(struct net_device *ndev)