mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-23 17:55:05 +03:00
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:
committed by
Revanth Kumar Uppala
parent
610e2b6a21
commit
7fb9f30e28
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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 */
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user