summaryrefslogtreecommitdiff
path: root/drivers/net/phy/phylink.c
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@armlinux.org.uk>2020-07-21 12:04:41 +0100
committerDavid S. Miller <davem@davemloft.net>2020-07-21 15:46:51 -0700
commitb7ad14c2fe2d4b2abee491e3adfa3d0123aa2d8c (patch)
tree0e30d2ffbf9f374ea79898118395a7e7f6f39ecc /drivers/net/phy/phylink.c
parent1571e700fd610c39e8b50b0110b1ee9badb2fe6a (diff)
net: phylink: re-implement interface configuration with PCS
With PCS support, how we implement interface reconfiguration (or other major reconfiguration) is not up to the job; we end up reconfiguring the PCS for an interface change while the link could potentially be up. In order to solve this, add two additional MAC methods for major configuration, one to prepare for the change, and one to finish the change. This allows mvneta and mvpp2 to shutdown what they require prior to the MAC and PCS configuration calls, and then restart as appropriate. This impacts ksettings_set(), which now needs to identify whether the change is a minor tweak to the advertisement masks or whether the interface mode has changed, and call the appropriate function for that update. Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/phy/phylink.c')
-rw-r--r--drivers/net/phy/phylink.c75
1 files changed, 52 insertions, 23 deletions
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index 84a426401102..d554a0fbb4f3 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -433,23 +433,47 @@ static void phylink_mac_pcs_an_restart(struct phylink *pl)
}
}
-static void phylink_pcs_config(struct phylink *pl, bool force_restart,
- const struct phylink_link_state *state)
+static void phylink_major_config(struct phylink *pl, bool restart,
+ const struct phylink_link_state *state)
{
- bool restart = force_restart;
+ int err;
+
+ phylink_dbg(pl, "major config %s\n", phy_modes(state->interface));
- if (pl->pcs_ops && pl->pcs_ops->pcs_config(pl->config,
- pl->cur_link_an_mode,
- state->interface,
- state->advertising,
- !!(pl->link_config.pause &
- MLO_PAUSE_AN)))
- restart = true;
+ if (pl->mac_ops->mac_prepare) {
+ err = pl->mac_ops->mac_prepare(pl->config, pl->cur_link_an_mode,
+ state->interface);
+ if (err < 0) {
+ phylink_err(pl, "mac_prepare failed: %pe\n",
+ ERR_PTR(err));
+ return;
+ }
+ }
phylink_mac_config(pl, state);
+ if (pl->pcs_ops) {
+ err = pl->pcs_ops->pcs_config(pl->config, pl->cur_link_an_mode,
+ state->interface,
+ state->advertising,
+ !!(pl->link_config.pause &
+ MLO_PAUSE_AN));
+ if (err < 0)
+ phylink_err(pl, "pcs_config failed: %pe\n",
+ ERR_PTR(err));
+ if (err > 0)
+ restart = true;
+ }
if (restart)
phylink_mac_pcs_an_restart(pl);
+
+ if (pl->mac_ops->mac_finish) {
+ err = pl->mac_ops->mac_finish(pl->config, pl->cur_link_an_mode,
+ state->interface);
+ if (err < 0)
+ phylink_err(pl, "mac_prepare failed: %pe\n",
+ ERR_PTR(err));
+ }
}
/*
@@ -555,7 +579,7 @@ static void phylink_mac_initial_config(struct phylink *pl, bool force_restart)
link_state.link = false;
phylink_apply_manual_flow(pl, &link_state);
- phylink_pcs_config(pl, force_restart, &link_state);
+ phylink_major_config(pl, force_restart, &link_state);
}
static const char *phylink_pause_to_str(int pause)
@@ -674,7 +698,7 @@ static void phylink_resolve(struct work_struct *w)
phylink_link_down(pl);
cur_link_state = false;
}
- phylink_pcs_config(pl, false, &link_state);
+ phylink_major_config(pl, false, &link_state);
pl->link_config.interface = link_state.interface;
} else if (!pl->pcs_ops) {
/* The interface remains unchanged, only the speed,
@@ -1450,21 +1474,26 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
return -EINVAL;
mutex_lock(&pl->state_mutex);
- linkmode_copy(pl->link_config.advertising, config.advertising);
- pl->link_config.interface = config.interface;
pl->link_config.speed = config.speed;
pl->link_config.duplex = config.duplex;
pl->link_config.an_enabled = config.an_enabled;
- if (pl->cur_link_an_mode == MLO_AN_INBAND &&
- !test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) {
- /* If in 802.3z mode, this updates the advertisement.
- *
- * If we are in SGMII mode without a PHY, there is no
- * advertisement; the only thing we have is the pause
- * modes which can only come from a PHY.
- */
- phylink_pcs_config(pl, true, &pl->link_config);
+ if (pl->link_config.interface != config.interface) {
+ /* The interface changed, e.g. 1000base-X <-> 2500base-X */
+ /* We need to force the link down, then change the interface */
+ if (pl->old_link_state) {
+ phylink_link_down(pl);
+ pl->old_link_state = false;
+ }
+ if (!test_bit(PHYLINK_DISABLE_STOPPED,
+ &pl->phylink_disable_state))
+ phylink_major_config(pl, false, &config);
+ pl->link_config.interface = config.interface;
+ linkmode_copy(pl->link_config.advertising, config.advertising);
+ } else if (!linkmode_equal(pl->link_config.advertising,
+ config.advertising)) {
+ linkmode_copy(pl->link_config.advertising, config.advertising);
+ phylink_change_inband_advert(pl);
}
mutex_unlock(&pl->state_mutex);