Commit a152c918 authored by David S. Miller's avatar David S. Miller

Merge branch 'eee-broken-modes'

Jerome Brunet says:

====================
Fix OdroidC2 Gigabit Tx link issue

This patchset fixes an issue with the OdroidC2 board (DWMAC + RTL8211F).
The platform seems to enter LPI on the Rx path too often while performing
relatively high TX transfer. This eventually break the link (both Tx and
Rx), and require to bring the interface down and up again to get the Rx
path working again.

The root cause of this issue is not fully understood yet but disabling EEE
advertisement on the PHY prevent this feature to be negotiated.
With this change, the link is stable and reliable, with the expected
throughput performance.

The patchset adds options in the generic phy driver to disable EEE
advertisement, through device tree. The way it is done is very similar
to the handling of the max-speed property.

Changes since V2: [2]
 - Rename "eee-advert-disable" to "eee-broken-modes" to make the intended
   purpose of this option clear (flag broken configuration, not a
   configuration option)
 - Add DT bindings constants so the DT configuration is more user friendly
 - Submit to net-next instead of net.

Changes since V1: [1]
 - Disable the advertisement of EEE in the generic code instead of the
   realtek driver.

[1] : http://lkml.kernel.org/r/1479220154-25851-1-git-send-email-jbrunet@baylibre.com
[2] : http://lkml.kernel.org/r/1479742524-30222-1-git-send-email-jbrunet@baylibre.com
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 436feafe c44a3bc1
...@@ -38,6 +38,8 @@ Optional Properties: ...@@ -38,6 +38,8 @@ Optional Properties:
- enet-phy-lane-swap: If set, indicates the PHY will swap the TX/RX lanes to - enet-phy-lane-swap: If set, indicates the PHY will swap the TX/RX lanes to
compensate for the board being designed with the lanes swapped. compensate for the board being designed with the lanes swapped.
- eee-broken-modes: Bits to clear in the MDIO_AN_EEE_ADV register to
disable EEE broken modes.
Example: Example:
......
...@@ -1396,6 +1396,9 @@ int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data) ...@@ -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); 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); phy_write_mmd_indirect(phydev, MDIO_AN_EEE_ADV, MDIO_MMD_AN, val);
return 0; return 0;
......
...@@ -1120,6 +1120,43 @@ static int genphy_config_advert(struct phy_device *phydev) ...@@ -1120,6 +1120,43 @@ static int genphy_config_advert(struct phy_device *phydev)
return changed; 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 * genphy_setup_forced - configures/forces speed/duplex from @phydev
* @phydev: target phy_device struct * @phydev: target phy_device struct
...@@ -1178,15 +1215,20 @@ EXPORT_SYMBOL(genphy_restart_aneg); ...@@ -1178,15 +1215,20 @@ EXPORT_SYMBOL(genphy_restart_aneg);
*/ */
int genphy_config_aneg(struct phy_device *phydev) int genphy_config_aneg(struct phy_device *phydev)
{ {
int result; int err, changed;
changed = genphy_config_eee_advert(phydev);
if (AUTONEG_ENABLE != phydev->autoneg) if (AUTONEG_ENABLE != phydev->autoneg)
return genphy_setup_forced(phydev); return genphy_setup_forced(phydev);
result = genphy_config_advert(phydev); err = genphy_config_advert(phydev);
if (result < 0) /* error */ if (err < 0) /* error */
return result; return err;
if (result == 0) {
changed |= err;
if (changed == 0) {
/* Advertisement hasn't changed, but maybe aneg was never on to /* Advertisement hasn't changed, but maybe aneg was never on to
* begin with? Or maybe phy was isolated? * begin with? Or maybe phy was isolated?
*/ */
...@@ -1196,16 +1238,16 @@ int genphy_config_aneg(struct phy_device *phydev) ...@@ -1196,16 +1238,16 @@ int genphy_config_aneg(struct phy_device *phydev)
return ctl; return ctl;
if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE)) 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 /* Only restart aneg if we are advertising something different
* than we were before. * than we were before.
*/ */
if (result > 0) if (changed > 0)
result = genphy_restart_aneg(phydev); return genphy_restart_aneg(phydev);
return result; return 0;
} }
EXPORT_SYMBOL(genphy_config_aneg); EXPORT_SYMBOL(genphy_config_aneg);
...@@ -1563,6 +1605,21 @@ static void of_set_phy_supported(struct phy_device *phydev) ...@@ -1563,6 +1605,21 @@ static void of_set_phy_supported(struct phy_device *phydev)
__set_phy_supported(phydev, max_speed); __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 * phy_probe - probe and init a PHY device
* @dev: device to probe and init * @dev: device to probe and init
...@@ -1600,6 +1657,11 @@ static int phy_probe(struct device *dev) ...@@ -1600,6 +1657,11 @@ static int phy_probe(struct device *dev)
of_set_phy_supported(phydev); of_set_phy_supported(phydev);
phydev->advertising = phydev->supported; 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 */ /* Set the state to READY by default */
phydev->state = PHY_READY; phydev->state = PHY_READY;
......
/*
* This header provides generic constants for ethernet MDIO bindings
*/
#ifndef _DT_BINDINGS_NET_MDIO_H
#define _DT_BINDINGS_NET_MDIO_H
/*
* EEE capability Advertisement
*/
#define MDIO_EEE_100TX 0x0002 /* 100TX EEE cap */
#define MDIO_EEE_1000T 0x0004 /* 1000T EEE cap */
#define MDIO_EEE_10GT 0x0008 /* 10GT EEE cap */
#define MDIO_EEE_1000KX 0x0010 /* 1000KX EEE cap */
#define MDIO_EEE_10GKX4 0x0020 /* 10G KX4 EEE cap */
#define MDIO_EEE_10GKR 0x0040 /* 10G KR EEE cap */
#endif
...@@ -417,6 +417,9 @@ struct phy_device { ...@@ -417,6 +417,9 @@ struct phy_device {
u32 advertising; u32 advertising;
u32 lp_advertising; u32 lp_advertising;
/* Energy efficient ethernet modes which should be prohibited */
u32 eee_broken_modes;
int autoneg; int autoneg;
int link_timeout; int link_timeout;
......
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