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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
*
|
||||
@@ -215,6 +270,7 @@ static void ether_adjust_link(struct net_device *dev)
|
||||
struct phy_device *phydev = pdata->phydev;
|
||||
int new_state = 0, speed_changed = 0;
|
||||
unsigned long val;
|
||||
unsigned int eee_enable = OSI_DISABLE;
|
||||
|
||||
if (phydev == NULL) {
|
||||
return;
|
||||
@@ -273,6 +329,13 @@ static void ether_adjust_link(struct net_device *dev)
|
||||
"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 */
|
||||
|
||||
/**
|
||||
* @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
|
||||
*
|
||||
@@ -1280,6 +1364,9 @@ static int ether_open(struct net_device *dev)
|
||||
goto err_r_irq;
|
||||
}
|
||||
|
||||
/* Init EEE configuration */
|
||||
ether_init_eee_params(pdata);
|
||||
|
||||
/* Start the MAC */
|
||||
osi_start_mac(pdata->osi_core);
|
||||
|
||||
|
||||
@@ -308,6 +308,14 @@ struct ether_priv_data {
|
||||
bool clks_enable;
|
||||
/** Delayed work queue to read RMON counters periodically */
|
||||
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);
|
||||
int ether_handle_priv_ts_ioctl(struct ether_priv_data *pdata,
|
||||
struct ifreq *ifr);
|
||||
int ether_conf_eee(struct ether_priv_data *pdata, unsigned int tx_lpi_enable);
|
||||
#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_vlan_frames_gb),
|
||||
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 */
|
||||
EQOS_MMC_STAT(mmc_rx_ipv4_gd),
|
||||
@@ -700,6 +708,211 @@ static int ether_get_coalesce(struct net_device *dev,
|
||||
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
|
||||
* 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,
|
||||
.get_wol = ether_get_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)
|
||||
|
||||
Reference in New Issue
Block a user