diff options
| author | Jakub Kicinski <kuba@kernel.org> | 2026-01-21 19:50:56 -0800 |
|---|---|---|
| committer | Jakub Kicinski <kuba@kernel.org> | 2026-01-21 19:50:57 -0800 |
| commit | 82a6c28d51c7467834e4bbf890f857b3bae0618f (patch) | |
| tree | 1aaa1a197a813d8c13eadcd454970cc54c274dae | |
| parent | 331cf8fc1855c2c750cbc3fb797e71ac8e4428cf (diff) | |
| parent | ae73535c9d0f166c1538df59198d10ce68ca99e8 (diff) | |
Merge branch 'phylink-link-callback-replay-helpers-for-sja1105-and-xpcs'
Vladimir Oltean says:
====================
Phylink link callback replay helpers for SJA1105 and XPCS
The sja1105 is reducing its direct interaction with the XPCS.
The changes presented here are an older simplification idea, broken out
of a previous patch set to allow for more thorough review.
====================
Link: https://patch.msgid.link/20260119121954.1624535-1-vladimir.oltean@nxp.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
| -rw-r--r-- | drivers/net/dsa/sja1105/sja1105_main.c | 84 | ||||
| -rw-r--r-- | drivers/net/phy/phylink.c | 75 | ||||
| -rw-r--r-- | include/linux/phylink.h | 5 |
3 files changed, 80 insertions, 84 deletions
diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index aa2145cf29a6..2a4a0fe20dae 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -1260,7 +1260,9 @@ static int sja1105_set_port_speed(struct sja1105_private *priv, int port, int speed_mbps) { struct sja1105_mac_config_entry *mac; + struct device *dev = priv->ds->dev; u64 speed; + int rc; /* On P/Q/R/S, one can read from the device via the MAC reconfiguration * tables. On E/T, MAC reconfig tables are not readable, only writable. @@ -1305,26 +1307,6 @@ static int sja1105_set_port_speed(struct sja1105_private *priv, int port, */ mac[port].speed = speed; - return 0; -} - -/* Write the MAC Configuration Table entry and, if necessary, the CGU settings, - * after a link speedchange for this port. - */ -static int sja1105_set_port_config(struct sja1105_private *priv, int port) -{ - struct sja1105_mac_config_entry *mac; - struct device *dev = priv->ds->dev; - int rc; - - /* On P/Q/R/S, one can read from the device via the MAC reconfiguration - * tables. On E/T, MAC reconfig tables are not readable, only writable. - * We have to *know* what the MAC looks like. For the sake of keeping - * the code common, we'll use the static configuration tables as a - * reasonable approximation for both E/T and P/Q/R/S. - */ - mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries; - /* Write to the dynamic reconfiguration tables */ rc = sja1105_dynamic_config_write(priv, BLK_IDX_MAC_CONFIG, port, &mac[port], true); @@ -1380,9 +1362,7 @@ static void sja1105_mac_link_up(struct phylink_config *config, struct sja1105_private *priv = dp->ds->priv; int port = dp->index; - if (!sja1105_set_port_speed(priv, port, speed)) - sja1105_set_port_config(priv, port); - + sja1105_set_port_speed(priv, port, speed); sja1105_inhibit_tx(priv, BIT(port), false); } @@ -2280,14 +2260,12 @@ int sja1105_static_config_reload(struct sja1105_private *priv, { struct ptp_system_timestamp ptp_sts_before; struct ptp_system_timestamp ptp_sts_after; - u16 bmcr[SJA1105_MAX_NUM_PORTS] = {0}; - u64 mac_speed[SJA1105_MAX_NUM_PORTS]; struct sja1105_mac_config_entry *mac; struct dsa_switch *ds = priv->ds; + struct dsa_port *dp; s64 t1, t2, t3, t4; - s64 t12, t34; - int rc, i; - s64 now; + s64 t12, t34, now; + int rc; mutex_lock(&priv->fdb_lock); mutex_lock(&priv->mgmt_lock); @@ -2299,13 +2277,9 @@ int sja1105_static_config_reload(struct sja1105_private *priv, * switch wants to see in the static config in order to allow us to * change it through the dynamic interface later. */ - for (i = 0; i < ds->num_ports; i++) { - mac_speed[i] = mac[i].speed; - mac[i].speed = priv->info->port_speed[SJA1105_SPEED_AUTO]; - - if (priv->pcs[i]) - bmcr[i] = mdiobus_c45_read(priv->mdio_pcs, i, - MDIO_MMD_VEND2, MDIO_CTRL1); + dsa_switch_for_each_available_port(dp, ds) { + phylink_replay_link_begin(dp->pl); + mac[dp->index].speed = priv->info->port_speed[SJA1105_SPEED_AUTO]; } /* No PTP operations can run right now */ @@ -2359,44 +2333,8 @@ int sja1105_static_config_reload(struct sja1105_private *priv, goto out; } - for (i = 0; i < ds->num_ports; i++) { - struct phylink_pcs *pcs = priv->pcs[i]; - unsigned int neg_mode; - - mac[i].speed = mac_speed[i]; - rc = sja1105_set_port_config(priv, i); - if (rc < 0) - goto out; - - if (!pcs) - continue; - - if (bmcr[i] & BMCR_ANENABLE) - neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED; - else - neg_mode = PHYLINK_PCS_NEG_OUTBAND; - - rc = pcs->ops->pcs_config(pcs, neg_mode, priv->phy_mode[i], - NULL, true); - if (rc < 0) - goto out; - - if (neg_mode == PHYLINK_PCS_NEG_OUTBAND) { - int speed = SPEED_UNKNOWN; - - if (priv->phy_mode[i] == PHY_INTERFACE_MODE_2500BASEX) - speed = SPEED_2500; - else if (bmcr[i] & BMCR_SPEED1000) - speed = SPEED_1000; - else if (bmcr[i] & BMCR_SPEED100) - speed = SPEED_100; - else - speed = SPEED_10; - - pcs->ops->pcs_link_up(pcs, neg_mode, priv->phy_mode[i], - speed, DUPLEX_FULL); - } - } + dsa_switch_for_each_available_port(dp, ds) + phylink_replay_link_end(dp->pl); rc = sja1105_reload_cbs(priv); if (rc < 0) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index c8fd6b91cdd4..7641f1f41e39 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -28,6 +28,7 @@ enum { PHYLINK_DISABLE_STOPPED, PHYLINK_DISABLE_LINK, PHYLINK_DISABLE_MAC_WOL, + PHYLINK_DISABLE_REPLAY, PCS_STATE_DOWN = 0, PCS_STATE_STARTING, @@ -77,6 +78,7 @@ struct phylink { bool link_failed; bool suspend_link_up; + bool force_major_config; bool major_config_failed; bool mac_supports_eee_ops; bool mac_supports_eee; @@ -1684,18 +1686,18 @@ static void phylink_resolve(struct work_struct *w) if (pl->act_link_an_mode != MLO_AN_FIXED) phylink_apply_manual_flow(pl, &link_state); - if (mac_config) { - if (link_state.interface != pl->link_config.interface) { - /* The interface has changed, force the link down and - * then reconfigure. - */ - if (cur_link_state) { - phylink_link_down(pl); - cur_link_state = false; - } - phylink_major_config(pl, false, &link_state); - pl->link_config.interface = link_state.interface; + if ((mac_config && link_state.interface != pl->link_config.interface) || + pl->force_major_config) { + /* The interface has changed or a forced major configuration + * was requested, so force the link down and then reconfigure. + */ + if (cur_link_state) { + phylink_link_down(pl); + cur_link_state = false; } + phylink_major_config(pl, false, &link_state); + pl->link_config.interface = link_state.interface; + pl->force_major_config = false; } /* If configuration of the interface failed, force the link down @@ -4359,6 +4361,57 @@ void phylink_mii_c45_pcs_get_state(struct mdio_device *pcs, } EXPORT_SYMBOL_GPL(phylink_mii_c45_pcs_get_state); +/** + * phylink_replay_link_begin() - begin replay of link callbacks for driver + * which loses state + * @pl: a pointer to a &struct phylink returned from phylink_create() + * + * Helper for MAC drivers which may perform a destructive reset at runtime. + * Both the own driver's mac_link_down() method is called, as well as the + * pcs_link_down() method of the split PCS (if any). + * + * This is similar to phylink_stop(), except it does not alter the state of + * the phylib PHY (it is assumed that it is not affected by the MAC destructive + * reset). + */ +void phylink_replay_link_begin(struct phylink *pl) +{ + ASSERT_RTNL(); + + phylink_run_resolve_and_disable(pl, PHYLINK_DISABLE_REPLAY); +} +EXPORT_SYMBOL_GPL(phylink_replay_link_begin); + +/** + * phylink_replay_link_end() - end replay of link callbacks for driver + * which lost state + * @pl: a pointer to a &struct phylink returned from phylink_create() + * + * Helper for MAC drivers which may perform a destructive reset at runtime. + * Both the own driver's mac_config() and mac_link_up() methods, as well as the + * pcs_config() and pcs_link_up() method of the split PCS (if any), are called. + * + * This is similar to phylink_start(), except it does not alter the state of + * the phylib PHY. + * + * One must call this method only within the same rtnl_lock() critical section + * as a previous phylink_replay_link_start(). + */ +void phylink_replay_link_end(struct phylink *pl) +{ + ASSERT_RTNL(); + + if (WARN(!test_bit(PHYLINK_DISABLE_REPLAY, + &pl->phylink_disable_state), + "phylink_replay_link_end() called without a prior phylink_replay_link_begin()\n")) + return; + + pl->force_major_config = true; + phylink_enable_and_run_resolve(pl, PHYLINK_DISABLE_REPLAY); + flush_work(&pl->resolve); +} +EXPORT_SYMBOL_GPL(phylink_replay_link_end); + static int __init phylink_init(void) { for (int i = 0; i < ARRAY_SIZE(phylink_sfp_interface_preference); ++i) diff --git a/include/linux/phylink.h b/include/linux/phylink.h index 20996f5778d1..2bc0db3d52ac 100644 --- a/include/linux/phylink.h +++ b/include/linux/phylink.h @@ -837,4 +837,9 @@ void phylink_mii_c45_pcs_get_state(struct mdio_device *pcs, void phylink_decode_usxgmii_word(struct phylink_link_state *state, uint16_t lpa); + +void phylink_replay_link_begin(struct phylink *pl); + +void phylink_replay_link_end(struct phylink *pl); + #endif |
