Commit c84786fa authored by Russell King's avatar Russell King Committed by David S. Miller

net: phy: marvell10g: read copper results from CSSR1

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: default avatarAndrew Lunn <andrew@lunn.ch>
Signed-off-by: default avatarRussell King <rmk+kernel@armlinux.org.uk>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent be64e397
...@@ -39,10 +39,19 @@ enum { ...@@ -39,10 +39,19 @@ enum {
MV_PCS_BASE_R = 0x1000, MV_PCS_BASE_R = 0x1000,
MV_PCS_1000BASEX = 0x2000, MV_PCS_1000BASEX = 0x2000,
MV_PCS_PAIRSWAP = 0x8182, MV_PCS_CSSR1 = 0x8008,
MV_PCS_PAIRSWAP_MASK = 0x0003, MV_PCS_CSSR1_SPD1_MASK = 0xc000,
MV_PCS_PAIRSWAP_AB = 0x0002, MV_PCS_CSSR1_SPD1_SPD2 = 0xc000,
MV_PCS_PAIRSWAP_NONE = 0x0003, 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 /* These registers appear at 0x800X and 0xa00X - the 0xa00X control
* registers appear to set themselves to the 0x800X when AN is * registers appear to set themselves to the 0x800X when AN is
...@@ -413,35 +422,18 @@ static void mv3310_update_interface(struct phy_device *phydev) ...@@ -413,35 +422,18 @@ static void mv3310_update_interface(struct phy_device *phydev)
} }
/* 10GBASE-ER,LR,LRM,SR do not support autonegotiation. */ /* 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->link = 1;
phydev->speed = SPEED_10000; phydev->speed = SPEED_10000;
phydev->duplex = DUPLEX_FULL; phydev->duplex = DUPLEX_FULL;
mv3310_update_interface(phydev);
return 0; return 0;
} }
static int mv3310_read_status(struct phy_device *phydev) static int mv3310_read_status_copper(struct phy_device *phydev)
{ {
int val; int cssr1, speed, 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);
val = genphy_c45_read_link(phydev); val = genphy_c45_read_link(phydev);
if (val < 0) if (val < 0)
...@@ -451,6 +443,52 @@ static int mv3310_read_status(struct phy_device *phydev) ...@@ -451,6 +443,52 @@ static int mv3310_read_status(struct phy_device *phydev)
if (val < 0) if (val < 0)
return val; 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) { if (val & MDIO_AN_STAT1_COMPLETE) {
val = genphy_c45_read_lpa(phydev); val = genphy_c45_read_lpa(phydev);
if (val < 0) if (val < 0)
...@@ -463,38 +501,37 @@ static int mv3310_read_status(struct phy_device *phydev) ...@@ -463,38 +501,37 @@ static int mv3310_read_status(struct phy_device *phydev)
mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, val); mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, val);
if (phydev->autoneg == AUTONEG_ENABLE) /* Update the pause status */
phy_resolve_aneg_linkmode(phydev); phy_resolve_aneg_pause(phydev);
} }
if (phydev->autoneg != AUTONEG_ENABLE) { return 0;
val = genphy_c45_read_pma(phydev); }
if (val < 0)
return val;
}
if (phydev->speed == SPEED_10000) { static int mv3310_read_status(struct phy_device *phydev)
val = genphy_c45_read_mdix(phydev); {
if (val < 0) int err, val;
return val;
} else { phydev->speed = SPEED_UNKNOWN;
val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_PAIRSWAP); 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;
val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_BASE_R + MDIO_STAT1);
if (val < 0) if (val < 0)
return val; return val;
switch (val & MV_PCS_PAIRSWAP_MASK) { if (val & MDIO_STAT1_LSTATUS)
case MV_PCS_PAIRSWAP_AB: err = mv3310_read_status_10gbaser(phydev);
phydev->mdix = ETH_TP_MDI_X; else
break; err = mv3310_read_status_copper(phydev);
case MV_PCS_PAIRSWAP_NONE: if (err < 0)
phydev->mdix = ETH_TP_MDI; return err;
break;
default:
phydev->mdix = ETH_TP_MDI_INVALID;
break;
}
}
if (phydev->link)
mv3310_update_interface(phydev); mv3310_update_interface(phydev);
return 0; return 0;
......
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