/* Flow control is resolved according to our and the link partners * advertisments using the following drawn from the 802.3 specs: * Local device Link partner * Pause AsymDir Pause AsymDir Result * 1 X 1 X TX+RX * 0 1 1 1 RX * 1 1 0 1 TX */ static void phylink_resolve_flow(struct phylink *pl, struct phylink_link_state *state) { int new_pause = 0; if (pl->link_config.pause & MLO_PAUSE_AN) { int pause = 0; if (phylink_test(pl->link_config.advertising, Pause)) pause |= MLO_PAUSE_SYM; if (phylink_test(pl->link_config.advertising, Asym_Pause)) pause |= MLO_PAUSE_ASYM; pause &= state->pause; if (pause & MLO_PAUSE_SYM) new_pause = MLO_PAUSE_TX | MLO_PAUSE_RX; else if (pause & MLO_PAUSE_ASYM) new_pause = state->pause & MLO_PAUSE_SYM ? MLO_PAUSE_RX : MLO_PAUSE_TX; } else { new_pause = pl->link_config.pause & MLO_PAUSE_TXRX_MASK; } state->pause &= ~MLO_PAUSE_TXRX_MASK; state->pause |= new_pause; }
static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy) { struct phylink_link_state config; __ETHTOOL_DECLARE_LINK_MODE_MASK(supported); u32 advertising; int ret; memset(&config, 0, sizeof(config)); ethtool_convert_legacy_u32_to_link_mode(supported, phy->supported); ethtool_convert_legacy_u32_to_link_mode(config.advertising, phy->advertising); config.interface = pl->link_config.interface; /* * This is the new way of dealing with flow control for PHYs, * as described by Timur Tabi in commit 529ed1275263 ("net: phy: * phy drivers should not set SUPPORTED_[Asym_]Pause") except * using our validate call to the MAC, we rely upon the MAC * clearing the bits from both supported and advertising fields. */ if (phylink_test(supported, Pause)) phylink_set(config.advertising, Pause); if (phylink_test(supported, Asym_Pause)) phylink_set(config.advertising, Asym_Pause); ret = phylink_validate(pl, supported, &config); if (ret) return ret; phy->phylink = pl; phy->phy_link_change = phylink_phy_change; netdev_info(pl->netdev, "PHY [%s] driver [%s]\n", dev_name(&phy->mdio.dev), phy->drv->name); mutex_lock(&phy->lock); mutex_lock(&pl->state_mutex); pl->netdev->phydev = phy; pl->phydev = phy; linkmode_copy(pl->supported, supported); linkmode_copy(pl->link_config.advertising, config.advertising); /* Restrict the phy advertisment according to the MAC support. */ ethtool_convert_link_mode_to_legacy_u32(&advertising, config.advertising); phy->advertising = advertising; mutex_unlock(&pl->state_mutex); mutex_unlock(&phy->lock); netdev_dbg(pl->netdev, "phy: setting supported %*pb advertising 0x%08x\n", __ETHTOOL_LINK_MODE_MASK_NBITS, pl->supported, phy->advertising); phy_start_machine(phy); if (phy->irq > 0) phy_start_interrupts(phy); return 0; }
int phylink_ethtool_set_pauseparam(struct phylink *pl, struct ethtool_pauseparam *pause) { struct phylink_link_state *config = &pl->link_config; WARN_ON(!lockdep_rtnl_is_held()); if (!phylink_test(pl->supported, Pause) && !phylink_test(pl->supported, Asym_Pause)) return -EOPNOTSUPP; if (!phylink_test(pl->supported, Asym_Pause) && !pause->autoneg && pause->rx_pause != pause->tx_pause) return -EINVAL; config->pause &= ~(MLO_PAUSE_AN | MLO_PAUSE_TXRX_MASK); if (pause->autoneg) config->pause |= MLO_PAUSE_AN; if (pause->rx_pause) config->pause |= MLO_PAUSE_RX; if (pause->tx_pause) config->pause |= MLO_PAUSE_TX; if (!test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) { switch (pl->link_an_mode) { case MLO_AN_PHY: /* Silently mark the carrier down, and then trigger a resolve */ netif_carrier_off(pl->netdev); phylink_run_resolve(pl); break; case MLO_AN_FIXED: /* Should we allow fixed links to change against the config? */ phylink_resolve_flow(pl, config); phylink_mac_config(pl, config); break; case MLO_AN_SGMII: case MLO_AN_8023Z: phylink_mac_config(pl, config); phylink_mac_an_restart(pl); break; } } return 0; }
/** * phylink_helper_basex_speed() - 1000BaseX/2500BaseX helper * @state: a pointer to a &struct phylink_link_state * * Inspect the interface mode, advertising mask or forced speed and * decide whether to run at 2.5Gbit or 1Gbit appropriately, switching * the interface mode to suit. @state->interface is appropriately * updated, and the advertising mask has the "other" baseX_Full flag * cleared. */ void phylink_helper_basex_speed(struct phylink_link_state *state) { if (phy_interface_mode_is_8023z(state->interface)) { bool want_2500 = state->an_enabled ? phylink_test(state->advertising, 2500baseX_Full) : state->speed == SPEED_2500; if (want_2500) { phylink_clear(state->advertising, 1000baseX_Full); state->interface = PHY_INTERFACE_MODE_2500BASEX; } else { phylink_clear(state->advertising, 2500baseX_Full); state->interface = PHY_INTERFACE_MODE_1000BASEX; } } }