Merge branch 'marvell-88x2222-improvements'
Ivan Bornyakov says: ==================== net: phy: marvell-88x2222: a couple of improvements First, there are some SFP modules that only uses RX_LOS for link indication. Add check that link is operational before actual read of line-side status. Second, it is invalid to set 10G speed without autonegotiation, according to phy_ethtool_ksettings_set(). Implement switching between 10GBase-R and 1000Base-X/SGMII if autonegotiation can't complete but there is signal in line. Changelog: v1 -> v2: * make checking that link is operational more friendly for trancievers without SFP cages. * split swapping 1G/10G modes into non-functional and functional commits for the sake of easier review. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Коммит
945c6ff851
|
@ -32,6 +32,10 @@
|
|||
#define MV_HOST_RST_SW BIT(7)
|
||||
#define MV_PORT_RST_SW (MV_LINE_RST_SW | MV_HOST_RST_SW)
|
||||
|
||||
/* PMD Receive Signal Detect */
|
||||
#define MV_RX_SIGNAL_DETECT 0x000A
|
||||
#define MV_RX_SIGNAL_DETECT_GLOBAL BIT(0)
|
||||
|
||||
/* 1000Base-X/SGMII Control Register */
|
||||
#define MV_1GBX_CTRL (0x2000 + MII_BMCR)
|
||||
|
||||
|
@ -48,9 +52,12 @@
|
|||
#define MV_1GBX_PHY_STAT_SPEED100 BIT(14)
|
||||
#define MV_1GBX_PHY_STAT_SPEED1000 BIT(15)
|
||||
|
||||
#define AUTONEG_TIMEOUT 3
|
||||
|
||||
struct mv2222_data {
|
||||
phy_interface_t line_interface;
|
||||
__ETHTOOL_DECLARE_LINK_MODE_MASK(supported);
|
||||
bool sfp_link;
|
||||
};
|
||||
|
||||
/* SFI PMA transmit enable */
|
||||
|
@ -81,86 +88,6 @@ static int mv2222_soft_reset(struct phy_device *phydev)
|
|||
5000, 1000000, true);
|
||||
}
|
||||
|
||||
/* Returns negative on error, 0 if link is down, 1 if link is up */
|
||||
static int mv2222_read_status_10g(struct phy_device *phydev)
|
||||
{
|
||||
int val, link = 0;
|
||||
|
||||
val = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_STAT1);
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
if (val & MDIO_STAT1_LSTATUS) {
|
||||
link = 1;
|
||||
|
||||
/* 10GBASE-R do not support auto-negotiation */
|
||||
phydev->autoneg = AUTONEG_DISABLE;
|
||||
phydev->speed = SPEED_10000;
|
||||
phydev->duplex = DUPLEX_FULL;
|
||||
}
|
||||
|
||||
return link;
|
||||
}
|
||||
|
||||
/* Returns negative on error, 0 if link is down, 1 if link is up */
|
||||
static int mv2222_read_status_1g(struct phy_device *phydev)
|
||||
{
|
||||
int val, link = 0;
|
||||
|
||||
val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_STAT);
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
if (!(val & BMSR_LSTATUS) ||
|
||||
(phydev->autoneg == AUTONEG_ENABLE &&
|
||||
!(val & BMSR_ANEGCOMPLETE)))
|
||||
return 0;
|
||||
|
||||
link = 1;
|
||||
|
||||
val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_PHY_STAT);
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
if (val & MV_1GBX_PHY_STAT_AN_RESOLVED) {
|
||||
if (val & MV_1GBX_PHY_STAT_DUPLEX)
|
||||
phydev->duplex = DUPLEX_FULL;
|
||||
else
|
||||
phydev->duplex = DUPLEX_HALF;
|
||||
|
||||
if (val & MV_1GBX_PHY_STAT_SPEED1000)
|
||||
phydev->speed = SPEED_1000;
|
||||
else if (val & MV_1GBX_PHY_STAT_SPEED100)
|
||||
phydev->speed = SPEED_100;
|
||||
else
|
||||
phydev->speed = SPEED_10;
|
||||
}
|
||||
|
||||
return link;
|
||||
}
|
||||
|
||||
static int mv2222_read_status(struct phy_device *phydev)
|
||||
{
|
||||
struct mv2222_data *priv = phydev->priv;
|
||||
int link;
|
||||
|
||||
phydev->link = 0;
|
||||
phydev->speed = SPEED_UNKNOWN;
|
||||
phydev->duplex = DUPLEX_UNKNOWN;
|
||||
|
||||
if (priv->line_interface == PHY_INTERFACE_MODE_10GBASER)
|
||||
link = mv2222_read_status_10g(phydev);
|
||||
else
|
||||
link = mv2222_read_status_1g(phydev);
|
||||
|
||||
if (link < 0)
|
||||
return link;
|
||||
|
||||
phydev->link = link;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mv2222_disable_aneg(struct phy_device *phydev)
|
||||
{
|
||||
int ret = phy_clear_bits_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_CTRL,
|
||||
|
@ -248,6 +175,24 @@ static bool mv2222_is_1gbx_capable(struct phy_device *phydev)
|
|||
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)
|
||||
{
|
||||
struct mv2222_data *priv = phydev->priv;
|
||||
|
@ -267,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;
|
||||
bool changed = false;
|
||||
|
@ -275,26 +221,24 @@ static int mv2222_setup_forced(struct phy_device *phydev)
|
|||
|
||||
switch (priv->line_interface) {
|
||||
case PHY_INTERFACE_MODE_10GBASER:
|
||||
if (phydev->speed == SPEED_1000 &&
|
||||
mv2222_is_1gbx_capable(phydev)) {
|
||||
if (mv2222_is_1gbx_capable(phydev)) {
|
||||
priv->line_interface = PHY_INTERFACE_MODE_1000BASEX;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (mv2222_is_sgmii_capable(phydev)) {
|
||||
priv->line_interface = PHY_INTERFACE_MODE_SGMII;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
break;
|
||||
case PHY_INTERFACE_MODE_1000BASEX:
|
||||
if (phydev->speed == SPEED_10000 &&
|
||||
mv2222_is_10g_capable(phydev)) {
|
||||
case PHY_INTERFACE_MODE_SGMII:
|
||||
if (mv2222_is_10g_capable(phydev)) {
|
||||
priv->line_interface = PHY_INTERFACE_MODE_10GBASER;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
break;
|
||||
case PHY_INTERFACE_MODE_SGMII:
|
||||
ret = mv2222_set_sgmii_speed(phydev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
@ -306,6 +250,29 @@ static int mv2222_setup_forced(struct phy_device *phydev)
|
|||
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);
|
||||
}
|
||||
|
||||
|
@ -319,17 +286,9 @@ static int mv2222_config_aneg(struct phy_device *phydev)
|
|||
return 0;
|
||||
|
||||
if (phydev->autoneg == AUTONEG_DISABLE ||
|
||||
phydev->speed == SPEED_10000)
|
||||
priv->line_interface == PHY_INTERFACE_MODE_10GBASER)
|
||||
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,
|
||||
ETHTOOL_LINK_MODE_1000baseX_Full_BIT);
|
||||
|
||||
|
@ -363,6 +322,135 @@ static int mv2222_aneg_done(struct phy_device *phydev)
|
|||
return (ret & BMSR_ANEGCOMPLETE);
|
||||
}
|
||||
|
||||
/* 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 timeout;
|
||||
int val, link = 0;
|
||||
|
||||
val = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_STAT1);
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
if (val & MDIO_STAT1_LSTATUS) {
|
||||
link = 1;
|
||||
|
||||
/* 10GBASE-R do not support auto-negotiation */
|
||||
phydev->autoneg = AUTONEG_DISABLE;
|
||||
phydev->speed = SPEED_10000;
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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 timeout;
|
||||
int val, link = 0;
|
||||
|
||||
val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_STAT);
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
if (phydev->autoneg == AUTONEG_ENABLE &&
|
||||
!(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;
|
||||
|
||||
link = 1;
|
||||
|
||||
val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_PHY_STAT);
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
if (val & MV_1GBX_PHY_STAT_AN_RESOLVED) {
|
||||
if (val & MV_1GBX_PHY_STAT_DUPLEX)
|
||||
phydev->duplex = DUPLEX_FULL;
|
||||
else
|
||||
phydev->duplex = DUPLEX_HALF;
|
||||
|
||||
if (val & MV_1GBX_PHY_STAT_SPEED1000)
|
||||
phydev->speed = SPEED_1000;
|
||||
else if (val & MV_1GBX_PHY_STAT_SPEED100)
|
||||
phydev->speed = SPEED_100;
|
||||
else
|
||||
phydev->speed = SPEED_10;
|
||||
}
|
||||
|
||||
return link;
|
||||
}
|
||||
|
||||
static bool mv2222_link_is_operational(struct phy_device *phydev)
|
||||
{
|
||||
struct mv2222_data *priv = phydev->priv;
|
||||
int val;
|
||||
|
||||
val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MV_RX_SIGNAL_DETECT);
|
||||
if (val < 0 || !(val & MV_RX_SIGNAL_DETECT_GLOBAL))
|
||||
return false;
|
||||
|
||||
if (phydev->sfp_bus && !priv->sfp_link)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int mv2222_read_status(struct phy_device *phydev)
|
||||
{
|
||||
struct mv2222_data *priv = phydev->priv;
|
||||
int link;
|
||||
|
||||
phydev->link = 0;
|
||||
phydev->speed = SPEED_UNKNOWN;
|
||||
phydev->duplex = DUPLEX_UNKNOWN;
|
||||
|
||||
if (!mv2222_link_is_operational(phydev))
|
||||
return 0;
|
||||
|
||||
if (priv->line_interface == PHY_INTERFACE_MODE_10GBASER)
|
||||
link = mv2222_read_status_10g(phydev);
|
||||
else
|
||||
link = mv2222_read_status_1g(phydev);
|
||||
|
||||
if (link < 0)
|
||||
return link;
|
||||
|
||||
phydev->link = link;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mv2222_resume(struct phy_device *phydev)
|
||||
{
|
||||
return mv2222_tx_enable(phydev);
|
||||
|
@ -424,11 +512,7 @@ static int mv2222_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
|
|||
return ret;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -446,9 +530,29 @@ static void mv2222_sfp_remove(void *upstream)
|
|||
linkmode_zero(priv->supported);
|
||||
}
|
||||
|
||||
static void mv2222_sfp_link_up(void *upstream)
|
||||
{
|
||||
struct phy_device *phydev = upstream;
|
||||
struct mv2222_data *priv;
|
||||
|
||||
priv = phydev->priv;
|
||||
priv->sfp_link = true;
|
||||
}
|
||||
|
||||
static void mv2222_sfp_link_down(void *upstream)
|
||||
{
|
||||
struct phy_device *phydev = upstream;
|
||||
struct mv2222_data *priv;
|
||||
|
||||
priv = phydev->priv;
|
||||
priv->sfp_link = false;
|
||||
}
|
||||
|
||||
static const struct sfp_upstream_ops sfp_phy_ops = {
|
||||
.module_insert = mv2222_sfp_insert,
|
||||
.module_remove = mv2222_sfp_remove,
|
||||
.link_up = mv2222_sfp_link_up,
|
||||
.link_down = mv2222_sfp_link_down,
|
||||
.attach = phy_sfp_attach,
|
||||
.detach = phy_sfp_detach,
|
||||
};
|
||||
|
|
Загрузка…
Ссылка в новой задаче