mirror of
https://github.com/torvalds/linux
synced 2024-10-06 19:34:19 +00:00
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:
commit
945c6ff851
|
@ -32,6 +32,10 @@
|
||||||
#define MV_HOST_RST_SW BIT(7)
|
#define MV_HOST_RST_SW BIT(7)
|
||||||
#define MV_PORT_RST_SW (MV_LINE_RST_SW | MV_HOST_RST_SW)
|
#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 */
|
/* 1000Base-X/SGMII Control Register */
|
||||||
#define MV_1GBX_CTRL (0x2000 + MII_BMCR)
|
#define MV_1GBX_CTRL (0x2000 + MII_BMCR)
|
||||||
|
|
||||||
|
@ -48,9 +52,12 @@
|
||||||
#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);
|
||||||
|
bool sfp_link;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* SFI PMA transmit enable */
|
/* SFI PMA transmit enable */
|
||||||
|
@ -81,86 +88,6 @@ static int mv2222_soft_reset(struct phy_device *phydev)
|
||||||
5000, 1000000, true);
|
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)
|
static int mv2222_disable_aneg(struct phy_device *phydev)
|
||||||
{
|
{
|
||||||
int ret = phy_clear_bits_mmd(phydev, MDIO_MMD_PCS, MV_1GBX_CTRL,
|
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);
|
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;
|
||||||
|
@ -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;
|
struct mv2222_data *priv = phydev->priv;
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
|
@ -275,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;
|
||||||
|
@ -306,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -319,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);
|
||||||
|
|
||||||
|
@ -363,6 +322,135 @@ static int mv2222_aneg_done(struct phy_device *phydev)
|
||||||
return (ret & BMSR_ANEGCOMPLETE);
|
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)
|
static int mv2222_resume(struct phy_device *phydev)
|
||||||
{
|
{
|
||||||
return mv2222_tx_enable(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;
|
return ret;
|
||||||
|
|
||||||
if (mutex_trylock(&phydev->lock)) {
|
if (mutex_trylock(&phydev->lock)) {
|
||||||
if (priv->line_interface == PHY_INTERFACE_MODE_10GBASER)
|
ret = mv2222_config_aneg(phydev);
|
||||||
ret = mv2222_setup_forced(phydev);
|
|
||||||
else
|
|
||||||
ret = mv2222_config_aneg(phydev);
|
|
||||||
|
|
||||||
mutex_unlock(&phydev->lock);
|
mutex_unlock(&phydev->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -446,9 +530,29 @@ static void mv2222_sfp_remove(void *upstream)
|
||||||
linkmode_zero(priv->supported);
|
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 = {
|
static const struct sfp_upstream_ops sfp_phy_ops = {
|
||||||
.module_insert = mv2222_sfp_insert,
|
.module_insert = mv2222_sfp_insert,
|
||||||
.module_remove = mv2222_sfp_remove,
|
.module_remove = mv2222_sfp_remove,
|
||||||
|
.link_up = mv2222_sfp_link_up,
|
||||||
|
.link_down = mv2222_sfp_link_down,
|
||||||
.attach = phy_sfp_attach,
|
.attach = phy_sfp_attach,
|
||||||
.detach = phy_sfp_detach,
|
.detach = phy_sfp_detach,
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue