Commit 6d0f77a0 authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'net-ethernet-rework-eee'

Oleksij Rempel says:

====================
net: ethernet: Rework EEE

with Andrew's permission I'll continue mainlining this patches:
==============================================================

Most MAC drivers get EEE wrong. The API to the PHY is not very
obvious, which is probably why. Rework the API, pushing most of the
EEE handling into phylib core, leaving the MAC drivers to just
enable/disable support for EEE in there change_link call back.

MAC drivers are now expect to indicate to phylib if they support
EEE. This will allow future patches to configure the PHY to advertise
no EEE link modes when EEE is not supported. The information could
also be used to enable SmartEEE if the PHY supports it.

With these changes, the uAPI configuration eee_enable becomes a global
on/off. tx-lpi must also be enabled before EEE is enabled. This fits
the discussion here:

https://lore.kernel.org/netdev/af880ce8-a7b8-138e-1ab9-8c89e662eecf@gmail.com/T/

This patchset puts in place all the infrastructure, and converts one
MAC driver to the new API. Following patchsets will convert other MAC
drivers, extend support into phylink, and when all MAC drivers are
converted to the new scheme, clean up some unneeded code.
====================

Link: https://lore.kernel.org/r/20240302195306.3207716-1-o.rempel@pengutronix.deSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 344f7a46 6a2495ad
......@@ -2017,6 +2017,37 @@ static int fec_get_mac(struct net_device *ndev)
/*
* Phy section
*/
/* LPI Sleep Ts count base on tx clk (clk_ref).
* The lpi sleep cnt value = X us / (cycle_ns).
*/
static int fec_enet_us_to_tx_cycle(struct net_device *ndev, int us)
{
struct fec_enet_private *fep = netdev_priv(ndev);
return us * (fep->clk_ref_rate / 1000) / 1000;
}
static int fec_enet_eee_mode_set(struct net_device *ndev, bool enable)
{
struct fec_enet_private *fep = netdev_priv(ndev);
struct ethtool_keee *p = &fep->eee;
unsigned int sleep_cycle, wake_cycle;
if (enable) {
sleep_cycle = fec_enet_us_to_tx_cycle(ndev, p->tx_lpi_timer);
wake_cycle = sleep_cycle;
} else {
sleep_cycle = 0;
wake_cycle = 0;
}
writel(sleep_cycle, fep->hwp + FEC_LPI_SLEEP);
writel(wake_cycle, fep->hwp + FEC_LPI_WAKE);
return 0;
}
static void fec_enet_adjust_link(struct net_device *ndev)
{
struct fec_enet_private *fep = netdev_priv(ndev);
......@@ -2056,6 +2087,8 @@ static void fec_enet_adjust_link(struct net_device *ndev)
netif_tx_unlock_bh(ndev);
napi_enable(&fep->napi);
}
if (fep->quirks & FEC_QUIRK_HAS_EEE)
fec_enet_eee_mode_set(ndev, phy_dev->enable_tx_lpi);
} else {
if (fep->link) {
netif_stop_queue(ndev);
......@@ -2415,6 +2448,9 @@ static int fec_enet_mii_probe(struct net_device *ndev)
else
phy_set_max_speed(phy_dev, 100);
if (fep->quirks & FEC_QUIRK_HAS_EEE)
phy_support_eee(phy_dev);
fep->link = 0;
fep->full_duplex = 0;
......@@ -3121,43 +3157,6 @@ static int fec_enet_set_coalesce(struct net_device *ndev,
return 0;
}
/* LPI Sleep Ts count base on tx clk (clk_ref).
* The lpi sleep cnt value = X us / (cycle_ns).
*/
static int fec_enet_us_to_tx_cycle(struct net_device *ndev, int us)
{
struct fec_enet_private *fep = netdev_priv(ndev);
return us * (fep->clk_ref_rate / 1000) / 1000;
}
static int fec_enet_eee_mode_set(struct net_device *ndev, bool enable)
{
struct fec_enet_private *fep = netdev_priv(ndev);
struct ethtool_keee *p = &fep->eee;
unsigned int sleep_cycle, wake_cycle;
int ret = 0;
if (enable) {
ret = phy_init_eee(ndev->phydev, false);
if (ret)
return ret;
sleep_cycle = fec_enet_us_to_tx_cycle(ndev, p->tx_lpi_timer);
wake_cycle = sleep_cycle;
} else {
sleep_cycle = 0;
wake_cycle = 0;
}
p->tx_lpi_enabled = enable;
writel(sleep_cycle, fep->hwp + FEC_LPI_SLEEP);
writel(wake_cycle, fep->hwp + FEC_LPI_WAKE);
return 0;
}
static int
fec_enet_get_eee(struct net_device *ndev, struct ethtool_keee *edata)
{
......@@ -3171,7 +3170,6 @@ fec_enet_get_eee(struct net_device *ndev, struct ethtool_keee *edata)
return -ENETDOWN;
edata->tx_lpi_timer = p->tx_lpi_timer;
edata->tx_lpi_enabled = p->tx_lpi_enabled;
return phy_ethtool_get_eee(ndev->phydev, edata);
}
......@@ -3181,7 +3179,6 @@ fec_enet_set_eee(struct net_device *ndev, struct ethtool_keee *edata)
{
struct fec_enet_private *fep = netdev_priv(ndev);
struct ethtool_keee *p = &fep->eee;
int ret = 0;
if (!(fep->quirks & FEC_QUIRK_HAS_EEE))
return -EOPNOTSUPP;
......@@ -3191,15 +3188,6 @@ fec_enet_set_eee(struct net_device *ndev, struct ethtool_keee *edata)
p->tx_lpi_timer = edata->tx_lpi_timer;
if (!edata->eee_enabled || !edata->tx_lpi_enabled ||
!edata->tx_lpi_timer)
ret = fec_enet_eee_mode_set(ndev, false);
else
ret = fec_enet_eee_mode_set(ndev, true);
if (ret)
return ret;
return phy_ethtool_set_eee(ndev->phydev, edata);
}
......
......@@ -1550,6 +1550,8 @@ EXPORT_SYMBOL(genphy_c45_ethtool_get_eee);
* advertised, but the previously advertised link modes are
* retained. This allows EEE to be enabled/disabled in a
* non-destructive way.
* Returns either error code, 0 if there was no change, or positive
* value if there was a change which triggered auto-neg.
*/
int genphy_c45_ethtool_set_eee(struct phy_device *phydev,
struct ethtool_keee *data)
......@@ -1576,8 +1578,16 @@ int genphy_c45_ethtool_set_eee(struct phy_device *phydev,
phydev->eee_enabled = data->eee_enabled;
ret = genphy_c45_an_config_eee_aneg(phydev);
if (ret > 0)
return phy_restart_aneg(phydev);
if (ret > 0) {
ret = phy_restart_aneg(phydev);
if (ret < 0)
return ret;
/* explicitly return 1, otherwise (ret > 0) value will be
* overwritten by phy_restart_aneg().
*/
return 1;
}
return ret;
}
......
......@@ -983,9 +983,17 @@ static int phy_check_link_status(struct phy_device *phydev)
if (phydev->link && phydev->state != PHY_RUNNING) {
phy_check_downshift(phydev);
phydev->state = PHY_RUNNING;
err = genphy_c45_eee_is_active(phydev,
NULL, NULL, NULL);
if (err < 0)
phydev->enable_tx_lpi = false;
else
phydev->enable_tx_lpi = (err & phydev->eee_cfg.tx_lpi_enabled);
phy_link_up(phydev);
} else if (!phydev->link && phydev->state != PHY_NOLINK) {
phydev->state = PHY_NOLINK;
phydev->enable_tx_lpi = false;
phy_link_down(phydev);
}
......@@ -1633,8 +1641,8 @@ EXPORT_SYMBOL(phy_get_eee_err);
* @phydev: target phy_device struct
* @data: ethtool_keee data
*
* Description: it reportes the Supported/Advertisement/LP Advertisement
* capabilities.
* Description: reports the Supported/Advertisement/LP Advertisement
* capabilities, etc.
*/
int phy_ethtool_get_eee(struct phy_device *phydev, struct ethtool_keee *data)
{
......@@ -1645,12 +1653,43 @@ int phy_ethtool_get_eee(struct phy_device *phydev, struct ethtool_keee *data)
mutex_lock(&phydev->lock);
ret = genphy_c45_ethtool_get_eee(phydev, data);
eeecfg_to_eee(data, &phydev->eee_cfg);
mutex_unlock(&phydev->lock);
return ret;
}
EXPORT_SYMBOL(phy_ethtool_get_eee);
/**
* phy_ethtool_set_eee_noneg - Adjusts MAC LPI configuration without PHY
* renegotiation
* @phydev: pointer to the target PHY device structure
* @data: pointer to the ethtool_keee structure containing the new EEE settings
*
* This function updates the Energy Efficient Ethernet (EEE) configuration
* for cases where only the MAC's Low Power Idle (LPI) configuration changes,
* without triggering PHY renegotiation. It ensures that the MAC is properly
* informed of the new LPI settings by cycling the link down and up, which
* is necessary for the MAC to adopt the new configuration. This adjustment
* is done only if there is a change in the tx_lpi_enabled or tx_lpi_timer
* configuration.
*/
static void phy_ethtool_set_eee_noneg(struct phy_device *phydev,
struct ethtool_keee *data)
{
if (phydev->eee_cfg.tx_lpi_enabled != data->tx_lpi_enabled ||
phydev->eee_cfg.tx_lpi_timer != data->tx_lpi_timer) {
eee_to_eeecfg(&phydev->eee_cfg, data);
phydev->enable_tx_lpi = eeecfg_mac_can_tx_lpi(&phydev->eee_cfg);
if (phydev->link) {
phydev->link = false;
phy_link_down(phydev);
phydev->link = true;
phy_link_up(phydev);
}
}
}
/**
* phy_ethtool_set_eee - set EEE supported and status
* @phydev: target phy_device struct
......@@ -1667,9 +1706,14 @@ int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_keee *data)
mutex_lock(&phydev->lock);
ret = genphy_c45_ethtool_set_eee(phydev, data);
if (ret >= 0) {
if (ret == 0)
phy_ethtool_set_eee_noneg(phydev, data);
eee_to_eeecfg(&phydev->eee_cfg, data);
}
mutex_unlock(&phydev->lock);
return ret;
return ret < 0 ? ret : 0;
}
EXPORT_SYMBOL(phy_ethtool_set_eee);
......
......@@ -2910,6 +2910,34 @@ void phy_advertise_eee_all(struct phy_device *phydev)
}
EXPORT_SYMBOL_GPL(phy_advertise_eee_all);
/**
* phy_support_eee - Set initial EEE policy configuration
* @phydev: Target phy_device struct
*
* This function configures the initial policy for Energy Efficient Ethernet
* (EEE) on the specified PHY device, influencing that EEE capabilities are
* advertised before the link is established. It should be called during PHY
* registration by the MAC driver and/or the PHY driver (for SmartEEE PHYs)
* if MAC supports LPI or PHY is capable to compensate missing LPI functionality
* of the MAC.
*
* The function sets default EEE policy parameters, including preparing the PHY
* to advertise EEE capabilities based on hardware support.
*
* It also sets the expected configuration for Low Power Idle (LPI) in the MAC
* driver. If the PHY framework determines that both local and remote
* advertisements support EEE, and the negotiated link mode is compatible with
* EEE, it will set enable_tx_lpi = true. The MAC driver is expected to act on
* this setting by enabling the LPI timer if enable_tx_lpi is set.
*/
void phy_support_eee(struct phy_device *phydev)
{
linkmode_copy(phydev->advertising_eee, phydev->supported_eee);
phydev->eee_cfg.tx_lpi_enabled = true;
phydev->eee_cfg.eee_enabled = true;
}
EXPORT_SYMBOL(phy_support_eee);
/**
* phy_support_sym_pause - Enable support of symmetrical pause
* @phydev: target phy_device struct
......
......@@ -30,6 +30,7 @@
#include <linux/refcount.h>
#include <linux/atomic.h>
#include <net/eee.h>
#define PHY_DEFAULT_FEATURES (SUPPORTED_Autoneg | \
SUPPORTED_TP | \
......@@ -594,6 +595,8 @@ struct macsec_ops;
* @supported_eee: supported PHY EEE linkmodes
* @advertising_eee: Currently advertised EEE linkmodes
* @eee_enabled: Flag indicating whether the EEE feature is enabled
* @enable_tx_lpi: When True, MAC should transmit LPI to PHY
* @eee_cfg: User configuration of EEE
* @lp_advertising: Current link partner advertised linkmodes
* @host_interfaces: PHY interface modes supported by host
* @eee_broken_modes: Energy efficient ethernet modes which should be prohibited
......@@ -703,7 +706,7 @@ struct phy_device {
__ETHTOOL_DECLARE_LINK_MODE_MASK(lp_advertising);
/* used with phy_speed_down */
__ETHTOOL_DECLARE_LINK_MODE_MASK(adv_old);
/* used for eee validation */
/* used for eee validation and configuration*/
__ETHTOOL_DECLARE_LINK_MODE_MASK(supported_eee);
__ETHTOOL_DECLARE_LINK_MODE_MASK(advertising_eee);
bool eee_enabled;
......@@ -713,6 +716,8 @@ struct phy_device {
/* Energy efficient ethernet modes which should be prohibited */
u32 eee_broken_modes;
bool enable_tx_lpi;
struct eee_config eee_cfg;
#ifdef CONFIG_LED_TRIGGER_PHY
struct phy_led_trigger *phy_led_triggers;
......@@ -1968,6 +1973,7 @@ void phy_advertise_supported(struct phy_device *phydev);
void phy_advertise_eee_all(struct phy_device *phydev);
void phy_support_sym_pause(struct phy_device *phydev);
void phy_support_asym_pause(struct phy_device *phydev);
void phy_support_eee(struct phy_device *phydev);
void phy_set_sym_pause(struct phy_device *phydev, bool rx, bool tx,
bool autoneg);
void phy_set_asym_pause(struct phy_device *phydev, bool rx, bool tx);
......
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef _EEE_H
#define _EEE_H
#include <linux/types.h>
struct eee_config {
u32 tx_lpi_timer;
bool tx_lpi_enabled;
bool eee_enabled;
};
static inline bool eeecfg_mac_can_tx_lpi(const struct eee_config *eeecfg)
{
/* eee_enabled is the master on/off */
if (!eeecfg->eee_enabled || !eeecfg->tx_lpi_enabled)
return false;
return true;
}
static inline void eeecfg_to_eee(struct ethtool_keee *eee,
const struct eee_config *eeecfg)
{
eee->tx_lpi_timer = eeecfg->tx_lpi_timer;
eee->tx_lpi_enabled = eeecfg->tx_lpi_enabled;
eee->eee_enabled = eeecfg->eee_enabled;
}
static inline void eee_to_eeecfg(struct eee_config *eeecfg,
const struct ethtool_keee *eee)
{
eeecfg->tx_lpi_timer = eee->tx_lpi_timer;
eeecfg->tx_lpi_enabled = eee->tx_lpi_enabled;
eeecfg->eee_enabled = eee->eee_enabled;
}
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment