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: Ivan Bornyakov <i.bornyakov@metrotek.ru> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
473960a7b4
commit
d7029f55cc
|
@ -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)
|
||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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,26 +221,24 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mv2222_is_sgmii_capable(phydev)) {
|
||||||
|
priv->line_interface = PHY_INTERFACE_MODE_SGMII;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case PHY_INTERFACE_MODE_1000BASEX:
|
case PHY_INTERFACE_MODE_1000BASEX:
|
||||||
if (phydev->speed == SPEED_10000 &&
|
case PHY_INTERFACE_MODE_SGMII:
|
||||||
mv2222_is_10g_capable(phydev)) {
|
if (mv2222_is_10g_capable(phydev)) {
|
||||||
priv->line_interface = PHY_INTERFACE_MODE_10GBASER;
|
priv->line_interface = PHY_INTERFACE_MODE_10GBASER;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
|
||||||
case PHY_INTERFACE_MODE_SGMII:
|
|
||||||
ret = mv2222_set_sgmii_speed(phydev);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -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)
|
||||||
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)
|
||||||
/* 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)
|
||||||
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)
|
||||||
/* 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)
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue