Skip to content

Commit

Permalink
net: phy: marvell10g: read copper results from CSSR1
Browse files Browse the repository at this point in the history
Read the copper autonegotiation results from the copper specific
status register, rather than decoding the advertisements. Reading
what the link is actually doing will allow us to support downshift
modes.

Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Russell King authored and davem330 committed Feb 27, 2020
1 parent be64e39 commit c84786f
Showing 1 changed file with 89 additions and 52 deletions.
141 changes: 89 additions & 52 deletions drivers/net/phy/marvell10g.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,19 @@ enum {
MV_PCS_BASE_R = 0x1000,
MV_PCS_1000BASEX = 0x2000,

MV_PCS_PAIRSWAP = 0x8182,
MV_PCS_PAIRSWAP_MASK = 0x0003,
MV_PCS_PAIRSWAP_AB = 0x0002,
MV_PCS_PAIRSWAP_NONE = 0x0003,
MV_PCS_CSSR1 = 0x8008,
MV_PCS_CSSR1_SPD1_MASK = 0xc000,
MV_PCS_CSSR1_SPD1_SPD2 = 0xc000,
MV_PCS_CSSR1_SPD1_1000 = 0x8000,
MV_PCS_CSSR1_SPD1_100 = 0x4000,
MV_PCS_CSSR1_SPD1_10 = 0x0000,
MV_PCS_CSSR1_DUPLEX_FULL= BIT(13),
MV_PCS_CSSR1_RESOLVED = BIT(11),
MV_PCS_CSSR1_MDIX = BIT(6),
MV_PCS_CSSR1_SPD2_MASK = 0x000c,
MV_PCS_CSSR1_SPD2_5000 = 0x0008,
MV_PCS_CSSR1_SPD2_2500 = 0x0004,
MV_PCS_CSSR1_SPD2_10000 = 0x0000,

/* These registers appear at 0x800X and 0xa00X - the 0xa00X control
* registers appear to set themselves to the 0x800X when AN is
Expand Down Expand Up @@ -413,35 +422,18 @@ static void mv3310_update_interface(struct phy_device *phydev)
}

/* 10GBASE-ER,LR,LRM,SR do not support autonegotiation. */
static int mv3310_read_10gbr_status(struct phy_device *phydev)
static int mv3310_read_status_10gbaser(struct phy_device *phydev)
{
phydev->link = 1;
phydev->speed = SPEED_10000;
phydev->duplex = DUPLEX_FULL;

mv3310_update_interface(phydev);

return 0;
}

static int mv3310_read_status(struct phy_device *phydev)
static int mv3310_read_status_copper(struct phy_device *phydev)
{
int val;

phydev->speed = SPEED_UNKNOWN;
phydev->duplex = DUPLEX_UNKNOWN;
linkmode_zero(phydev->lp_advertising);
phydev->link = 0;
phydev->pause = 0;
phydev->asym_pause = 0;
phydev->mdix = 0;

val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_BASE_R + MDIO_STAT1);
if (val < 0)
return val;

if (val & MDIO_STAT1_LSTATUS)
return mv3310_read_10gbr_status(phydev);
int cssr1, speed, val;

val = genphy_c45_read_link(phydev);
if (val < 0)
Expand All @@ -451,6 +443,52 @@ static int mv3310_read_status(struct phy_device *phydev)
if (val < 0)
return val;

cssr1 = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_CSSR1);
if (cssr1 < 0)
return val;

/* If the link settings are not resolved, mark the link down */
if (!(cssr1 & MV_PCS_CSSR1_RESOLVED)) {
phydev->link = 0;
return 0;
}

/* Read the copper link settings */
speed = cssr1 & MV_PCS_CSSR1_SPD1_MASK;
if (speed == MV_PCS_CSSR1_SPD1_SPD2)
speed |= cssr1 & MV_PCS_CSSR1_SPD2_MASK;

switch (speed) {
case MV_PCS_CSSR1_SPD1_SPD2 | MV_PCS_CSSR1_SPD2_10000:
phydev->speed = SPEED_10000;
break;

case MV_PCS_CSSR1_SPD1_SPD2 | MV_PCS_CSSR1_SPD2_5000:
phydev->speed = SPEED_5000;
break;

case MV_PCS_CSSR1_SPD1_SPD2 | MV_PCS_CSSR1_SPD2_2500:
phydev->speed = SPEED_2500;
break;

case MV_PCS_CSSR1_SPD1_1000:
phydev->speed = SPEED_1000;
break;

case MV_PCS_CSSR1_SPD1_100:
phydev->speed = SPEED_100;
break;

case MV_PCS_CSSR1_SPD1_10:
phydev->speed = SPEED_10;
break;
}

phydev->duplex = cssr1 & MV_PCS_CSSR1_DUPLEX_FULL ?
DUPLEX_FULL : DUPLEX_HALF;
phydev->mdix = cssr1 & MV_PCS_CSSR1_MDIX ?
ETH_TP_MDI_X : ETH_TP_MDI;

if (val & MDIO_AN_STAT1_COMPLETE) {
val = genphy_c45_read_lpa(phydev);
if (val < 0)
Expand All @@ -463,39 +501,38 @@ static int mv3310_read_status(struct phy_device *phydev)

mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, val);

if (phydev->autoneg == AUTONEG_ENABLE)
phy_resolve_aneg_linkmode(phydev);
/* Update the pause status */
phy_resolve_aneg_pause(phydev);
}

if (phydev->autoneg != AUTONEG_ENABLE) {
val = genphy_c45_read_pma(phydev);
if (val < 0)
return val;
}
return 0;
}

if (phydev->speed == SPEED_10000) {
val = genphy_c45_read_mdix(phydev);
if (val < 0)
return val;
} else {
val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_PAIRSWAP);
if (val < 0)
return val;
static int mv3310_read_status(struct phy_device *phydev)
{
int err, val;

switch (val & MV_PCS_PAIRSWAP_MASK) {
case MV_PCS_PAIRSWAP_AB:
phydev->mdix = ETH_TP_MDI_X;
break;
case MV_PCS_PAIRSWAP_NONE:
phydev->mdix = ETH_TP_MDI;
break;
default:
phydev->mdix = ETH_TP_MDI_INVALID;
break;
}
}
phydev->speed = SPEED_UNKNOWN;
phydev->duplex = DUPLEX_UNKNOWN;
linkmode_zero(phydev->lp_advertising);
phydev->link = 0;
phydev->pause = 0;
phydev->asym_pause = 0;
phydev->mdix = ETH_TP_MDI_INVALID;

mv3310_update_interface(phydev);
val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_BASE_R + MDIO_STAT1);
if (val < 0)
return val;

if (val & MDIO_STAT1_LSTATUS)
err = mv3310_read_status_10gbaser(phydev);
else
err = mv3310_read_status_copper(phydev);
if (err < 0)
return err;

if (phydev->link)
mv3310_update_interface(phydev);

return 0;
}
Expand Down

0 comments on commit c84786f

Please sign in to comment.