Skip to content

Commit

Permalink
net: phy: add an option to disable EEE advertisement
Browse files Browse the repository at this point in the history
This patch adds an option to disable EEE advertisement in the generic PHY
by providing a mask of prohibited modes corresponding to the value found in
the MDIO_AN_EEE_ADV register.

On some platforms, PHY Low power idle seems to be causing issues, even
breaking the link some cases. The patch provides a convenient way for these
platforms to disable EEE advertisement and work around the issue.

Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Tested-by: Yegor Yefremov <yegorslists@googlemail.com>
Tested-by: Andreas Färber <afaerber@suse.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
jbrunet authored and David S. Miller committed Nov 30, 2016
1 parent 436feaf commit d853d14
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 9 deletions.
3 changes: 3 additions & 0 deletions drivers/net/phy/phy.c
Original file line number Diff line number Diff line change
Expand Up @@ -1396,6 +1396,9 @@ int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data)
{
int val = ethtool_adv_to_mmd_eee_adv_t(data->advertised);

/* Mask prohibited EEE modes */
val &= ~phydev->eee_broken_modes;

phy_write_mmd_indirect(phydev, MDIO_AN_EEE_ADV, MDIO_MMD_AN, val);

return 0;
Expand Down
80 changes: 71 additions & 9 deletions drivers/net/phy/phy_device.c
Original file line number Diff line number Diff line change
Expand Up @@ -1120,6 +1120,43 @@ static int genphy_config_advert(struct phy_device *phydev)
return changed;
}

/**
* genphy_config_eee_advert - disable unwanted eee mode advertisement
* @phydev: target phy_device struct
*
* Description: Writes MDIO_AN_EEE_ADV after disabling unsupported energy
* efficent ethernet modes. Returns 0 if the PHY's advertisement hasn't
* changed, and 1 if it has changed.
*/
static int genphy_config_eee_advert(struct phy_device *phydev)
{
u32 broken = phydev->eee_broken_modes;
u32 old_adv, adv;

/* Nothing to disable */
if (!broken)
return 0;

/* If the following call fails, we assume that EEE is not
* supported by the phy. If we read 0, EEE is not advertised
* In both case, we don't need to continue
*/
adv = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_ADV, MDIO_MMD_AN);
if (adv <= 0)
return 0;

old_adv = adv;
adv &= ~broken;

/* Advertising remains unchanged with the broken mask */
if (old_adv == adv)
return 0;

phy_write_mmd_indirect(phydev, MDIO_AN_EEE_ADV, MDIO_MMD_AN, adv);

return 1;
}

/**
* genphy_setup_forced - configures/forces speed/duplex from @phydev
* @phydev: target phy_device struct
Expand Down Expand Up @@ -1178,15 +1215,20 @@ EXPORT_SYMBOL(genphy_restart_aneg);
*/
int genphy_config_aneg(struct phy_device *phydev)
{
int result;
int err, changed;

changed = genphy_config_eee_advert(phydev);

if (AUTONEG_ENABLE != phydev->autoneg)
return genphy_setup_forced(phydev);

result = genphy_config_advert(phydev);
if (result < 0) /* error */
return result;
if (result == 0) {
err = genphy_config_advert(phydev);
if (err < 0) /* error */
return err;

changed |= err;

if (changed == 0) {
/* Advertisement hasn't changed, but maybe aneg was never on to
* begin with? Or maybe phy was isolated?
*/
Expand All @@ -1196,16 +1238,16 @@ int genphy_config_aneg(struct phy_device *phydev)
return ctl;

if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE))
result = 1; /* do restart aneg */
changed = 1; /* do restart aneg */
}

/* Only restart aneg if we are advertising something different
* than we were before.
*/
if (result > 0)
result = genphy_restart_aneg(phydev);
if (changed > 0)
return genphy_restart_aneg(phydev);

return result;
return 0;
}
EXPORT_SYMBOL(genphy_config_aneg);

Expand Down Expand Up @@ -1563,6 +1605,21 @@ static void of_set_phy_supported(struct phy_device *phydev)
__set_phy_supported(phydev, max_speed);
}

static void of_set_phy_eee_broken(struct phy_device *phydev)
{
struct device_node *node = phydev->mdio.dev.of_node;
u32 broken;

if (!IS_ENABLED(CONFIG_OF_MDIO))
return;

if (!node)
return;

if (!of_property_read_u32(node, "eee-broken-modes", &broken))
phydev->eee_broken_modes = broken;
}

/**
* phy_probe - probe and init a PHY device
* @dev: device to probe and init
Expand Down Expand Up @@ -1600,6 +1657,11 @@ static int phy_probe(struct device *dev)
of_set_phy_supported(phydev);
phydev->advertising = phydev->supported;

/* Get the EEE modes we want to prohibit. We will ask
* the PHY stop advertising these mode later on
*/
of_set_phy_eee_broken(phydev);

/* Set the state to READY by default */
phydev->state = PHY_READY;

Expand Down
3 changes: 3 additions & 0 deletions include/linux/phy.h
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,9 @@ struct phy_device {
u32 advertising;
u32 lp_advertising;

/* Energy efficient ethernet modes which should be prohibited */
u32 eee_broken_modes;

int autoneg;

int link_timeout;
Expand Down

0 comments on commit d853d14

Please sign in to comment.