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