axgbe: fix link issues for gigabit external SFP PHYs and 100/1000 fiber modules

Within the code path of autonegotiation for gigabit SFP modules was a bug, causing
a report of LINK_ERR for cases where an external SFP PHY was present. Fixing this issue
did not resolve to a link however, as it turned out that while autonegotiation interrupts
were happening, it's resulting status cannot be correctly determined in all cases. In these
specific cases we have no other option than to assume a module has negotiated to 1Gbit/s.

PHY-specific configuration has been delegated to the miibus driver, if an external PHY is present.
It's possible that the i2c bus does not recognize a PHY on the first pass, so in all cases we
retry up to a maximum of 5 times during each link poll pass to ensure we didn't miss the presence
of an external PHY.

This commit also addresses link issues on both 100 mbit and 1Gb fiber modules. Not all of these modules
have the correct data set according to SFF-8472, as such we first check for gigabit compliance and
the associated baudrate, otherwise we resort back to determining what type of fiber module is plugged
in by checking the baudrate, cable length and wavelength and setting the MAC speed accordingly.
This commit is contained in:
Stephan de Wit 2023-04-26 11:49:59 +00:00 committed by Franco Fichtner
parent 316a02fe19
commit bf084b8bac
3 changed files with 98 additions and 26 deletions

View file

@ -440,7 +440,7 @@ xgbe_i2c_xfer(struct xgbe_prv_data *pdata, struct xgbe_i2c_op *op)
ret, state->tx_abort_source);
if (ret) {
axgbe_error("%s: i2c xfer ret %d abrt_source 0x%x \n", __func__,
axgbe_printf(1, "%s: i2c xfer ret %d abrt_source 0x%x \n", __func__,
ret, state->tx_abort_source);
if (state->tx_abort_source & IC_TX_ABRT_7B_ADDR_NOACK)
ret = -ENOTCONN;

View file

@ -734,11 +734,6 @@ xgbe_an37_state_machine(struct xgbe_prv_data *pdata)
if (pdata->an_int & XGBE_AN_CL37_INT_CMPLT) {
pdata->an_state = XGBE_AN_COMPLETE;
pdata->an_int &= ~XGBE_AN_CL37_INT_CMPLT;
/* If SGMII is enabled, check the link status */
if ((pdata->an_mode == XGBE_AN_MODE_CL37_SGMII) &&
!(pdata->an_status & XGBE_SGMII_AN_LINK_STATUS))
pdata->an_state = XGBE_AN_NO_LINK;
}
axgbe_printf(2, "%s: CL37 AN %s\n", __func__,
@ -1364,6 +1359,7 @@ xgbe_phy_status(struct xgbe_prv_data *pdata)
if (test_bit(XGBE_LINK_ERR, &pdata->dev_state)) {
axgbe_error("%s: LINK_ERR\n", __func__);
pdata->phy.link = 0;
clear_bit(XGBE_LINK_ERR, &pdata->dev_state);
goto adjust_link;
}

View file

@ -150,6 +150,9 @@ struct mtx xgbe_phy_comm_lock;
/* RRC frequency during link status check */
#define XGBE_RRC_FREQUENCY 10
/* SFP port max PHY probe retries */
#define XGBE_SFP_PHY_RETRY_MAX 5
enum xgbe_port_mode {
XGBE_PORT_MODE_RSVD = 0,
XGBE_PORT_MODE_BACKPLANE,
@ -186,6 +189,11 @@ enum xgbe_sfp_cable {
enum xgbe_sfp_base {
XGBE_SFP_BASE_UNKNOWN = 0,
XGBE_SFP_BASE_PX,
XGBE_SFP_BASE_BX10,
XGBE_SFP_BASE_100_FX,
XGBE_SFP_BASE_100_LX10,
XGBE_SFP_BASE_100_BX,
XGBE_SFP_BASE_1000_T,
XGBE_SFP_BASE_1000_SX,
XGBE_SFP_BASE_1000_LX,
@ -200,6 +208,7 @@ enum xgbe_sfp_base {
enum xgbe_sfp_speed {
XGBE_SFP_SPEED_UNKNOWN = 0,
XGBE_SFP_SPEED_100,
XGBE_SFP_SPEED_100_1000,
XGBE_SFP_SPEED_1000,
XGBE_SFP_SPEED_10000,
@ -226,12 +235,18 @@ enum xgbe_sfp_speed {
#define XGBE_SFP_BASE_1GBE_CC_LX BIT(1)
#define XGBE_SFP_BASE_1GBE_CC_CX BIT(2)
#define XGBE_SFP_BASE_1GBE_CC_T BIT(3)
#define XGBE_SFP_BASE_100M_CC_LX10 BIT(4)
#define XGBE_SFP_BASE_100M_CC_FX BIT(5)
#define XGBE_SFP_BASE_CC_BX10 BIT(6)
#define XGBE_SFP_BASE_CC_PX BIT(7)
#define XGBE_SFP_BASE_CABLE 8
#define XGBE_SFP_BASE_CABLE_PASSIVE BIT(2)
#define XGBE_SFP_BASE_CABLE_ACTIVE BIT(3)
#define XGBE_SFP_BASE_BR 12
#define XGBE_SFP_BASE_BR_100M_MIN 0x1
#define XGBE_SFP_BASE_BR_100M_MAX 0x2
#define XGBE_SFP_BASE_BR_1GBE_MIN 0x0a
#define XGBE_SFP_BASE_BR_1GBE_MAX 0x0d
#define XGBE_SFP_BASE_BR_10GBE_MIN 0x64
@ -387,6 +402,7 @@ struct xgbe_phy_data {
enum xgbe_mdio_reset mdio_reset;
unsigned int mdio_reset_addr;
unsigned int mdio_reset_gpio;
int sfp_phy_retries;
/* Re-driver support */
unsigned int redrv;
@ -404,6 +420,8 @@ struct xgbe_phy_data {
static enum xgbe_an_mode xgbe_phy_an_mode(struct xgbe_prv_data *pdata);
static int xgbe_phy_reset(struct xgbe_prv_data *pdata);
static int axgbe_ifmedia_upd(struct ifnet *ifp);
static void axgbe_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr);
static int
xgbe_phy_i2c_xfer(struct xgbe_prv_data *pdata, struct xgbe_i2c_op *i2c_op)
@ -778,6 +796,14 @@ xgbe_phy_sfp_phy_settings(struct xgbe_prv_data *pdata)
}
switch (phy_data->sfp_base) {
case XGBE_SFP_BASE_100_FX:
case XGBE_SFP_BASE_100_LX10:
case XGBE_SFP_BASE_100_BX:
pdata->phy.speed = SPEED_100;
pdata->phy.duplex = DUPLEX_FULL;
pdata->phy.autoneg = AUTONEG_DISABLE;
pdata->phy.pause_autoneg = AUTONEG_DISABLE;
break;
case XGBE_SFP_BASE_1000_T:
case XGBE_SFP_BASE_1000_SX:
case XGBE_SFP_BASE_1000_LX:
@ -800,6 +826,7 @@ xgbe_phy_sfp_phy_settings(struct xgbe_prv_data *pdata)
}
break;
case XGBE_SFP_BASE_1000_BX:
case XGBE_SFP_BASE_PX:
pdata->phy.speed = SPEED_1000;
pdata->phy.duplex = DUPLEX_FULL;
pdata->phy.autoneg = AUTONEG_DISABLE;
@ -873,6 +900,10 @@ xgbe_phy_sfp_bit_rate(struct xgbe_sfp_eeprom *sfp_eeprom,
sfp_base = sfp_eeprom->base;
switch (sfp_speed) {
case XGBE_SFP_SPEED_100:
min = XGBE_SFP_BASE_BR_100M_MIN;
max = XGBE_SFP_BASE_BR_100M_MAX;
break;
case XGBE_SFP_SPEED_1000:
min = XGBE_SFP_BASE_BR_1GBE_MIN;
max = XGBE_SFP_BASE_BR_1GBE_MAX;
@ -896,6 +927,11 @@ xgbe_phy_free_phy_device(struct xgbe_prv_data *pdata)
if (phy_data->phydev)
phy_data->phydev = 0;
if (pdata->axgbe_miibus != NULL) {
device_delete_child(pdata->dev, pdata->axgbe_miibus);
pdata->axgbe_miibus = NULL;
}
}
static bool
@ -1071,7 +1107,7 @@ xgbe_phy_start_aneg(struct xgbe_prv_data *pdata)
if (changed > 0) {
ret = xgbe_phy_mii_read(pdata, pdata->mdio_addr, MII_BMCR);
if (ret)
if (ret < 0)
return (ret);
ret = xgbe_phy_mii_write(pdata, pdata->mdio_addr, MII_BMCR,
@ -1131,7 +1167,6 @@ xgbe_phy_find_phy_device(struct xgbe_prv_data *pdata)
phy_data->phydev = 1;
xgbe_phy_external_phy_quirks(pdata);
xgbe_phy_start_aneg(pdata);
return (0);
}
@ -1144,24 +1179,36 @@ xgbe_phy_sfp_external_phy(struct xgbe_prv_data *pdata)
axgbe_printf(3, "%s: sfp_changed: 0x%x\n", __func__,
phy_data->sfp_changed);
if (!phy_data->sfp_changed)
return;
if (!phy_data->sfp_phy_retries) {
if (!phy_data->sfp_changed)
return;
}
phy_data->sfp_phy_avail = 0;
if (phy_data->sfp_base != XGBE_SFP_BASE_1000_T)
return;
/* Check access to the PHY by reading CTRL1 */
ret = xgbe_phy_i2c_mii_read(pdata, MII_BMCR);
if (ret < 0) {
axgbe_error("%s: ext phy fail %d\n", __func__, ret);
phy_data->sfp_phy_retries++;
if (phy_data->sfp_phy_retries >= XGBE_SFP_PHY_RETRY_MAX)
phy_data->sfp_phy_retries = 0;
axgbe_printf(1, "%s: ext phy fail %d. retrying.\n", __func__, ret);
return;
}
/* Successfully accessed the PHY */
phy_data->sfp_phy_avail = 1;
axgbe_printf(3, "Successfully accessed External PHY\n");
/* Attach external PHY to the miibus */
ret = mii_attach(pdata->dev, &pdata->axgbe_miibus, pdata->netdev,
(ifm_change_cb_t)axgbe_ifmedia_upd,
(ifm_stat_cb_t)axgbe_ifmedia_sts, BMSR_DEFCAPMASK,
pdata->mdio_addr, MII_OFFSET_ANY, MIIF_FORCEANEG);
if (ret) {
axgbe_error("mii attach failed with err=(%d)\n", ret);
}
}
static bool
@ -1268,16 +1315,41 @@ xgbe_phy_sfp_parse_eeprom(struct xgbe_prv_data *pdata)
phy_data->sfp_base = XGBE_SFP_BASE_1000_CX;
else if (sfp_base[XGBE_SFP_BASE_1GBE_CC] & XGBE_SFP_BASE_1GBE_CC_T)
phy_data->sfp_base = XGBE_SFP_BASE_1000_T;
else if (sfp_base[XGBE_SFP_BASE_1GBE_CC] & XGBE_SFP_BASE_100M_CC_LX10)
phy_data->sfp_base = XGBE_SFP_BASE_100_LX10;
else if (sfp_base[XGBE_SFP_BASE_1GBE_CC] & XGBE_SFP_BASE_100M_CC_FX)
phy_data->sfp_base = XGBE_SFP_BASE_100_FX;
else if (sfp_base[XGBE_SFP_BASE_1GBE_CC] & XGBE_SFP_BASE_CC_BX10) {
/* BX10 can be either 100 or 1000 */
if (xgbe_phy_sfp_bit_rate(sfp_eeprom, XGBE_SFP_SPEED_100)) {
phy_data->sfp_base = XGBE_SFP_BASE_100_BX;
} else {
/* default to 1000 */
phy_data->sfp_base = XGBE_SFP_BASE_1000_BX;
}
}
else if (sfp_base[XGBE_SFP_BASE_1GBE_CC] & XGBE_SFP_BASE_CC_PX)
phy_data->sfp_base = XGBE_SFP_BASE_PX;
else if (xgbe_phy_sfp_bit_rate(sfp_eeprom, XGBE_SFP_SPEED_1000)
&& (sfp_base[XGBE_SFP_BASE_SM_LEN_KM] >= XGBE_SFP_BASE_SM_LEN_KM_MIN
|| sfp_base[XGBE_SFP_BASE_SM_LEN_100M] >= XGBE_SFP_BASE_SM_LEN_100M_MIN)
&& wavelen >= XGBE_SFP_BASE_OSC_1310)
phy_data->sfp_base = XGBE_SFP_BASE_1000_BX;
else if (xgbe_phy_sfp_bit_rate(sfp_eeprom, XGBE_SFP_SPEED_100)
&& (sfp_base[XGBE_SFP_BASE_SM_LEN_KM] >= XGBE_SFP_BASE_SM_LEN_KM_MIN
|| sfp_base[XGBE_SFP_BASE_SM_LEN_100M] >= XGBE_SFP_BASE_SM_LEN_100M_MIN)
&& wavelen >= XGBE_SFP_BASE_OSC_1310)
phy_data->sfp_base = XGBE_SFP_BASE_100_BX;
switch (phy_data->sfp_base) {
case XGBE_SFP_BASE_100_FX:
case XGBE_SFP_BASE_100_LX10:
case XGBE_SFP_BASE_100_BX:
phy_data->sfp_speed = XGBE_SFP_SPEED_100;
case XGBE_SFP_BASE_1000_T:
phy_data->sfp_speed = XGBE_SFP_SPEED_100_1000;
break;
case XGBE_SFP_BASE_PX:
case XGBE_SFP_BASE_1000_SX:
case XGBE_SFP_BASE_1000_LX:
case XGBE_SFP_BASE_1000_CX:
@ -1375,14 +1447,15 @@ xgbe_phy_sfp_read_eeprom(struct xgbe_prv_data *pdata)
&eeprom_addr, sizeof(eeprom_addr),
&sfp_eeprom, sizeof(sfp_eeprom));
eeprom = &sfp_eeprom;
base = eeprom->base;
dump_sfp_eeprom(pdata, base);
if (ret) {
axgbe_error("I2C error reading SFP EEPROM\n");
goto put;
}
eeprom = &sfp_eeprom;
base = eeprom->base;
dump_sfp_eeprom(pdata, base);
/* Validate the contents read */
if (!xgbe_phy_sfp_verify_eeprom(sfp_eeprom.base[XGBE_SFP_BASE_CC],
sfp_eeprom.base, sizeof(sfp_eeprom.base) - 1)) {
@ -1785,17 +1858,17 @@ xgbe_phy_an37_sgmii_outcome(struct xgbe_prv_data *pdata)
}
break;
case XGBE_SGMII_AN_LINK_SPEED_1000:
default:
/* Default to 1000 */
if (pdata->an_status & XGBE_SGMII_AN_LINK_DUPLEX) {
XGBE_SET_LP_ADV(&pdata->phy, 1000baseT_Full);
mode = XGBE_MODE_SGMII_1000;
} else {
/* Half-duplex not supported */
XGBE_SET_LP_ADV(&pdata->phy, 1000baseT_Half);
mode = XGBE_MODE_UNKNOWN;
mode = XGBE_MODE_SGMII_1000;
}
break;
default:
mode = XGBE_MODE_UNKNOWN;
}
return (mode);
@ -2088,7 +2161,6 @@ xgbe_phy_an_config(struct xgbe_prv_data *pdata)
if (!phy_data->phydev)
return (0);
ret = xgbe_phy_start_aneg(pdata);
return (ret);
}
@ -2654,7 +2726,7 @@ xgbe_phy_get_type(struct xgbe_prv_data *pdata, struct ifmediareq * ifmr)
if(phy_data->port_mode == XGBE_PORT_MODE_NBASE_T)
ifmr->ifm_active |= IFM_100_T;
else if(phy_data->port_mode == XGBE_PORT_MODE_SFP)
ifmr->ifm_active |= IFM_1000_SGMII;
ifmr->ifm_active |= IFM_100_SGMII;
else
ifmr->ifm_active |= IFM_OTHER;
break;
@ -2849,7 +2921,8 @@ xgbe_phy_valid_speed_sfp_mode(struct xgbe_phy_data *phy_data, int speed)
switch (speed) {
case SPEED_100:
return (phy_data->sfp_speed == XGBE_SFP_SPEED_100_1000);
return ((phy_data->sfp_speed == XGBE_SFP_SPEED_100) ||
(phy_data->sfp_speed == XGBE_SFP_SPEED_100_1000));
case SPEED_1000:
return ((phy_data->sfp_speed == XGBE_SFP_SPEED_100_1000) ||
(phy_data->sfp_speed == XGBE_SFP_SPEED_1000));
@ -3068,10 +3141,12 @@ xgbe_phy_link_status(struct xgbe_prv_data *pdata, int *an_restart)
phy_data->sfp_rx_los);
return (0);
}
} else {
}
if (phy_data->phydev || phy_data->port_mode != XGBE_PORT_MODE_SFP) {
mii = device_get_softc(pdata->axgbe_miibus);
mii_tick(mii);
ret = xgbe_phy_read_status(pdata);
if (ret) {
axgbe_printf(2, "Link: Read status returned %d\n", ret);
@ -3577,6 +3652,7 @@ xgbe_phy_start(struct xgbe_prv_data *pdata)
axgbe_printf(0, "Checking GPIO expander validity\n");
xgbe_phy_validate_gpio_expander(pdata);
phy_data->sfp_phy_retries = 0;
xgbe_phy_sfp_detect(pdata);
break;
default: