summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@armlinux.org.uk>2020-03-31 11:07:10 +0100
committerRussell King <rmk+kernel@armlinux.org.uk>2020-06-29 01:19:01 +0100
commita7e4766500b71d059c0bfc59e6540f6d3b8c3833 (patch)
tree51fe13a9e7964e3280c321f6dce7d61bf9afa3bf /drivers
parentab177c909859cd8a8fc892318a9ad0a2f5552718 (diff)
net: phylink: re-implement interface configuration with PCS
With PCS support, how we implement interface 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 interface 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>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/phy/phylink.c80
1 files changed, 54 insertions, 26 deletions
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index b755a5325b8b..61b5225a1219 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_change_interface(struct phylink *pl, bool restart,
+ const struct phylink_link_state *state)
{
- bool restart = force_restart;
+ int err;
+
+ phylink_dbg(pl, "change interface %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_change_interface(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_change_interface(pl, false, &link_state);
pl->link_config.interface = link_state.interface;
} else if (!pl->pcs_ops) {
/* The interface remains unchanged, only the speed,
@@ -1451,22 +1475,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 = kset->base.autoneg !=
- AUTONEG_DISABLE;
-
- 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);
+ pl->link_config.an_enabled = kset->base.autoneg != AUTONEG_DISABLE;
+
+ 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_change_interface(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);