Skip to content

Commit

Permalink
net: phylink: add support for disabling in-band-status for base-x
Browse files Browse the repository at this point in the history
MediaTek LynxI PCS, 2500Base-X will only work without inband status due to
hardware limitation.

I understand this patch probably will not get approved as it is now, but
perhaps with some pointers in the correct direction to follow, I can change
it so it could be. It does however get the result that the rtl8221b on a
sfp module functions correctly, with and without (as optical sfp) the phy
attached and without using a sfp-quirk/ethtool to disable auto-negotiation.

Introduce bool phylink_basex_no_inband(pl), a function similar to
bool phylink_phy_no_inband(phy). An option could be to use a function like
bool pcs->ops->basex_no_inband(interface), where if the function-pointer is
null, it means it is supported. This instead of using the
of_device_is_compatible() function.

Code changed in phylink_pcs_neg_mode():

When there is no PHY attached, pl->pcs_neg_mode is set to
PHYLINK_PCS_NEG_INBAND_DISABLED. link_config is changed:
- advertising ETHTOOL_LINK_MODE_Autoneg_BIT cleared
- speed set according to interface
- duplex to DUPLEX_FULL
- pause to MLO_PAUSE_NONE

When there is a PHY attached, pl->cur_link_an_mode is set to MLO_AN_PHY.
To have the pcs function correctly with the rtl8221b, we need to do the
following to the in-band-status. We need to disable it when interface of
the pcs is set to 2500base-x, but need it enable it when switched to sgmii.

Changes to be committed:
	modified:   drivers/net/phy/phylink.c

Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
  • Loading branch information
ericwoud committed Jan 9, 2024
1 parent 5abf1e2 commit d5bdf45
Showing 1 changed file with 55 additions and 22 deletions.
77 changes: 55 additions & 22 deletions drivers/net/phy/phylink.c
Original file line number Diff line number Diff line change
Expand Up @@ -1074,6 +1074,20 @@ static void phylink_pcs_an_restart(struct phylink *pl)
pl->pcs->ops->pcs_an_restart(pl->pcs);
}

/* This function needs to be changed, not using compatible */
static bool phylink_basex_no_inband(struct phylink *pl)
{
struct device_node *node = pl->config->dev->of_node;

if (!node)
return false;

if (!of_device_is_compatible(node, "mediatek,eth-mac"))
return false;

return true;
}

/**
* phylink_pcs_neg_mode() - helper to determine PCS inband mode
* @mode: one of %MLO_AN_FIXED, %MLO_AN_PHY, %MLO_AN_INBAND.
Expand All @@ -1093,11 +1107,24 @@ static void phylink_pcs_an_restart(struct phylink *pl)
* Note: this is for cases where the PCS itself is involved in negotiation
* (e.g. Clause 37, SGMII and similar) not Clause 73.
*/
static unsigned int phylink_pcs_neg_mode(unsigned int mode,
phy_interface_t interface,
const unsigned long *advertising)
static void phylink_pcs_neg_mode(struct phylink *pl,
phy_interface_t interface,
const unsigned long *advertising)
{
unsigned int neg_mode;
if ((!!pl->phydev) && phylink_basex_no_inband(pl)) {
switch (interface) {
case PHY_INTERFACE_MODE_1000BASEX:
case PHY_INTERFACE_MODE_2500BASEX:
if (pl->cur_link_an_mode == MLO_AN_INBAND)
pl->cur_link_an_mode = MLO_AN_PHY;
break;
default:
/* restore mode if it was changed before */
if (pl->cur_link_an_mode == MLO_AN_PHY &&
pl->cfg_link_an_mode == MLO_AN_INBAND)
pl->cur_link_an_mode = pl->cfg_link_an_mode;
}
}

switch (interface) {
case PHY_INTERFACE_MODE_SGMII:
Expand All @@ -1109,10 +1136,10 @@ static unsigned int phylink_pcs_neg_mode(unsigned int mode,
* inband communication. Note: there exist PHYs that run
* with SGMII but do not send the inband data.
*/
if (!phylink_autoneg_inband(mode))
neg_mode = PHYLINK_PCS_NEG_OUTBAND;
if (!phylink_autoneg_inband(pl->cur_link_an_mode))
pl->pcs_neg_mode = PHYLINK_PCS_NEG_OUTBAND;
else
neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED;
pl->pcs_neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED;
break;

case PHY_INTERFACE_MODE_1000BASEX:
Expand All @@ -1123,21 +1150,30 @@ static unsigned int phylink_pcs_neg_mode(unsigned int mode,
* as well, but drivers may not support this, so may
* need to override this.
*/
if (!phylink_autoneg_inband(mode))
neg_mode = PHYLINK_PCS_NEG_OUTBAND;
else if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
advertising))
neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED;
else
neg_mode = PHYLINK_PCS_NEG_INBAND_DISABLED;
if (!phylink_autoneg_inband(pl->cur_link_an_mode)) {
pl->pcs_neg_mode = PHYLINK_PCS_NEG_OUTBAND;
} else if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
advertising) &&
!phylink_basex_no_inband(pl)) {
pl->pcs_neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED;
} else {
pl->pcs_neg_mode = PHYLINK_PCS_NEG_INBAND_DISABLED;
linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
pl->link_config.advertising);
pl->link_config.speed = (interface ==
PHY_INTERFACE_MODE_1000BASEX) ?
SPEED_1000 : SPEED_2500;
pl->link_config.duplex = DUPLEX_FULL;
pl->link_config.pause = MLO_PAUSE_NONE; /* ????? */
}
break;

default:
neg_mode = PHYLINK_PCS_NEG_NONE;
pl->pcs_neg_mode = PHYLINK_PCS_NEG_NONE;
break;
}

return neg_mode;
return;
}

static void phylink_major_config(struct phylink *pl, bool restart,
Expand All @@ -1151,9 +1187,7 @@ static void phylink_major_config(struct phylink *pl, bool restart,

phylink_dbg(pl, "major config %s\n", phy_modes(state->interface));

pl->pcs_neg_mode = phylink_pcs_neg_mode(pl->cur_link_an_mode,
state->interface,
state->advertising);
phylink_pcs_neg_mode(pl, state->interface, state->advertising);

if (pl->using_mac_select_pcs) {
pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface);
Expand Down Expand Up @@ -1257,9 +1291,8 @@ static int phylink_change_inband_advert(struct phylink *pl)
pl->link_config.pause);

/* Recompute the PCS neg mode */
pl->pcs_neg_mode = phylink_pcs_neg_mode(pl->cur_link_an_mode,
pl->link_config.interface,
pl->link_config.advertising);
phylink_pcs_neg_mode(pl, pl->link_config.interface,
pl->link_config.advertising);

neg_mode = pl->cur_link_an_mode;
if (pl->pcs->neg_mode)
Expand Down

0 comments on commit d5bdf45

Please sign in to comment.