Commit d7029f55 authored by Ivan Bornyakov's avatar Ivan Bornyakov Committed by David S. Miller

net: phy: marvell-88x2222: swap 1G/10G modes on autoneg

Setting 10G without autonegotiation is invalid according to
phy_ethtool_ksettings_set(). Thus, we need to set it during
autonegotiation.

If 1G autonegotiation can't complete for quite a time, but there is
signal in line, switch line interface type to 10GBase-R, if supported,
in hope for link to be established.

And vice versa. If 10GBase-R link can't be established for quite a time,
and autonegotiation is enabled, and there is signal in line, switch line
interface type to appropriate 1G mode, i.e. 1000Base-X or SGMII, if
supported.
Signed-off-by: default avatarIvan Bornyakov <i.bornyakov@metrotek.ru>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 473960a7
...@@ -52,6 +52,8 @@ ...@@ -52,6 +52,8 @@
#define MV_1GBX_PHY_STAT_SPEED100 BIT(14) #define MV_1GBX_PHY_STAT_SPEED100 BIT(14)
#define MV_1GBX_PHY_STAT_SPEED1000 BIT(15) #define MV_1GBX_PHY_STAT_SPEED1000 BIT(15)
#define AUTONEG_TIMEOUT 3
struct mv2222_data { struct mv2222_data {
phy_interface_t line_interface; phy_interface_t line_interface;
__ETHTOOL_DECLARE_LINK_MODE_MASK(supported); __ETHTOOL_DECLARE_LINK_MODE_MASK(supported);
...@@ -173,6 +175,24 @@ static bool mv2222_is_1gbx_capable(struct phy_device *phydev) ...@@ -173,6 +175,24 @@ static bool mv2222_is_1gbx_capable(struct phy_device *phydev)
priv->supported); priv->supported);
} }
static bool mv2222_is_sgmii_capable(struct phy_device *phydev)
{
struct mv2222_data *priv = phydev->priv;
return (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
priv->supported) ||
linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
priv->supported) ||
linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
priv->supported) ||
linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT,
priv->supported) ||
linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT,
priv->supported) ||
linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT,
priv->supported));
}
static int mv2222_config_line(struct phy_device *phydev) static int mv2222_config_line(struct phy_device *phydev)
{ {
struct mv2222_data *priv = phydev->priv; struct mv2222_data *priv = phydev->priv;
...@@ -192,7 +212,8 @@ static int mv2222_config_line(struct phy_device *phydev) ...@@ -192,7 +212,8 @@ static int mv2222_config_line(struct phy_device *phydev)
} }
} }
static int mv2222_setup_forced(struct phy_device *phydev) /* Switch between 1G (1000Base-X/SGMII) and 10G (10GBase-R) modes */
static int mv2222_swap_line_type(struct phy_device *phydev)
{ {
struct mv2222_data *priv = phydev->priv; struct mv2222_data *priv = phydev->priv;
bool changed = false; bool changed = false;
...@@ -200,25 +221,23 @@ static int mv2222_setup_forced(struct phy_device *phydev) ...@@ -200,25 +221,23 @@ static int mv2222_setup_forced(struct phy_device *phydev)
switch (priv->line_interface) { switch (priv->line_interface) {
case PHY_INTERFACE_MODE_10GBASER: case PHY_INTERFACE_MODE_10GBASER:
if (phydev->speed == SPEED_1000 && if (mv2222_is_1gbx_capable(phydev)) {
mv2222_is_1gbx_capable(phydev)) {
priv->line_interface = PHY_INTERFACE_MODE_1000BASEX; priv->line_interface = PHY_INTERFACE_MODE_1000BASEX;
changed = true; changed = true;
} }
break; if (mv2222_is_sgmii_capable(phydev)) {
case PHY_INTERFACE_MODE_1000BASEX: priv->line_interface = PHY_INTERFACE_MODE_SGMII;
if (phydev->speed == SPEED_10000 &&
mv2222_is_10g_capable(phydev)) {
priv->line_interface = PHY_INTERFACE_MODE_10GBASER;
changed = true; changed = true;
} }
break; break;
case PHY_INTERFACE_MODE_1000BASEX:
case PHY_INTERFACE_MODE_SGMII: case PHY_INTERFACE_MODE_SGMII:
ret = mv2222_set_sgmii_speed(phydev); if (mv2222_is_10g_capable(phydev)) {
if (ret < 0) priv->line_interface = PHY_INTERFACE_MODE_10GBASER;
return ret; changed = true;
}
break; break;
default: default:
...@@ -231,6 +250,29 @@ static int mv2222_setup_forced(struct phy_device *phydev) ...@@ -231,6 +250,29 @@ static int mv2222_setup_forced(struct phy_device *phydev)
return ret; return ret;
} }
return 0;
}
static int mv2222_setup_forced(struct phy_device *phydev)
{
struct mv2222_data *priv = phydev->priv;
int ret;
if (priv->line_interface == PHY_INTERFACE_MODE_10GBASER) {
if (phydev->speed < SPEED_10000 &&
phydev->speed != SPEED_UNKNOWN) {
ret = mv2222_swap_line_type(phydev);
if (ret < 0)
return ret;
}
}
if (priv->line_interface == PHY_INTERFACE_MODE_SGMII) {
ret = mv2222_set_sgmii_speed(phydev);
if (ret < 0)
return ret;
}
return mv2222_disable_aneg(phydev); return mv2222_disable_aneg(phydev);
} }
...@@ -244,17 +286,9 @@ static int mv2222_config_aneg(struct phy_device *phydev) ...@@ -244,17 +286,9 @@ static int mv2222_config_aneg(struct phy_device *phydev)
return 0; return 0;
if (phydev->autoneg == AUTONEG_DISABLE || if (phydev->autoneg == AUTONEG_DISABLE ||
phydev->speed == SPEED_10000) priv->line_interface == PHY_INTERFACE_MODE_10GBASER)
return mv2222_setup_forced(phydev); return mv2222_setup_forced(phydev);
if (priv->line_interface == PHY_INTERFACE_MODE_10GBASER &&
mv2222_is_1gbx_capable(phydev)) {
priv->line_interface = PHY_INTERFACE_MODE_1000BASEX;
ret = mv2222_config_line(phydev);
if (ret < 0)
return ret;
}
adv = linkmode_adv_to_mii_adv_x(priv->supported, adv = linkmode_adv_to_mii_adv_x(priv->supported,
ETHTOOL_LINK_MODE_1000baseX_Full_BIT); ETHTOOL_LINK_MODE_1000baseX_Full_BIT);
...@@ -291,6 +325,7 @@ static int mv2222_aneg_done(struct phy_device *phydev) ...@@ -291,6 +325,7 @@ static int mv2222_aneg_done(struct phy_device *phydev)
/* Returns negative on error, 0 if link is down, 1 if link is up */ /* Returns negative on error, 0 if link is down, 1 if link is up */
static int mv2222_read_status_10g(struct phy_device *phydev) static int mv2222_read_status_10g(struct phy_device *phydev)
{ {
static int timeout;
int val, link = 0; int val, link = 0;
val = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_STAT1); val = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_STAT1);
...@@ -304,6 +339,20 @@ static int mv2222_read_status_10g(struct phy_device *phydev) ...@@ -304,6 +339,20 @@ static int mv2222_read_status_10g(struct phy_device *phydev)
phydev->autoneg = AUTONEG_DISABLE; phydev->autoneg = AUTONEG_DISABLE;
phydev->speed = SPEED_10000; phydev->speed = SPEED_10000;
phydev->duplex = DUPLEX_FULL; phydev->duplex = DUPLEX_FULL;
} else {
if (phydev->autoneg == AUTONEG_ENABLE) {
timeout++;
if (timeout > AUTONEG_TIMEOUT) {
timeout = 0;
val = mv2222_swap_line_type(phydev);
if (val < 0)
return val;
return mv2222_config_aneg(phydev);
}
}
} }
return link; return link;
...@@ -312,15 +361,31 @@ static int mv2222_read_status_10g(struct phy_device *phydev) ...@@ -312,15 +361,31 @@ static int mv2222_read_status_10g(struct phy_device *phydev)
/* Returns negative on error, 0 if link is down, 1 if link is up */ /* Returns negative on error, 0 if link is down, 1 if link is up */
static int mv2222_read_status_1g(struct phy_device *phydev) static int mv2222_read_status_1g(struct phy_device *phydev)
{ {
static int timeout;
int val, link = 0; int val, link = 0;
val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_STAT); val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_STAT);
if (val < 0) if (val < 0)
return val; return val;
if (!(val & BMSR_LSTATUS) || if (phydev->autoneg == AUTONEG_ENABLE &&
(phydev->autoneg == AUTONEG_ENABLE && !(val & BMSR_ANEGCOMPLETE)) {
!(val & BMSR_ANEGCOMPLETE))) timeout++;
if (timeout > AUTONEG_TIMEOUT) {
timeout = 0;
val = mv2222_swap_line_type(phydev);
if (val < 0)
return val;
return mv2222_config_aneg(phydev);
}
return 0;
}
if (!(val & BMSR_LSTATUS))
return 0; return 0;
link = 1; link = 1;
...@@ -447,11 +512,7 @@ static int mv2222_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) ...@@ -447,11 +512,7 @@ static int mv2222_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
return ret; return ret;
if (mutex_trylock(&phydev->lock)) { if (mutex_trylock(&phydev->lock)) {
if (priv->line_interface == PHY_INTERFACE_MODE_10GBASER)
ret = mv2222_setup_forced(phydev);
else
ret = mv2222_config_aneg(phydev); ret = mv2222_config_aneg(phydev);
mutex_unlock(&phydev->lock); mutex_unlock(&phydev->lock);
} }
......
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