diff options
Diffstat (limited to 'drivers/net/phy/phylink.c')
-rw-r--r-- | drivers/net/phy/phylink.c | 1228 |
1 files changed, 722 insertions, 506 deletions
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 8a3728d423e7..aff3313d4de7 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -56,9 +56,11 @@ struct phylink { struct phy_device *phydev; phy_interface_t link_interface; /* PHY_INTERFACE_xxx */ u8 cfg_link_an_mode; /* MLO_AN_xxx */ - u8 cur_link_an_mode; + u8 req_link_an_mode; /* Requested MLO_AN_xxx mode */ + u8 act_link_an_mode; /* Active MLO_AN_xxx mode */ u8 link_port; /* The current non-phy ethtool port */ __ETHTOOL_DECLARE_LINK_MODE_MASK(supported); + __ETHTOOL_DECLARE_LINK_MODE_MASK(supported_lpi); /* The link configuration settings */ struct phylink_link_state link_config; @@ -74,22 +76,26 @@ struct phylink { struct mutex state_mutex; struct phylink_link_state phy_state; + unsigned int phy_ib_mode; struct work_struct resolve; unsigned int pcs_neg_mode; unsigned int pcs_state; - bool mac_link_dropped; - bool using_mac_select_pcs; + bool link_failed; + bool mac_supports_eee_ops; + bool mac_supports_eee; + bool phy_enable_tx_lpi; + bool mac_enable_tx_lpi; + bool mac_tx_clk_stop; + u32 mac_tx_lpi_timer; struct sfp_bus *sfp_bus; bool sfp_may_have_phy; DECLARE_PHY_INTERFACE_MASK(sfp_interfaces); __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support); - u8 sfp_link_an_mode; u8 sfp_port; struct eee_config eee_cfg; - bool eee_active; }; #define phylink_printk(level, pl, fmt, ...) \ @@ -179,6 +185,24 @@ static const char *phylink_an_mode_str(unsigned int mode) return mode < ARRAY_SIZE(modestr) ? modestr[mode] : "unknown"; } +static const char *phylink_pcs_mode_str(unsigned int mode) +{ + if (!mode) + return "none"; + + if (mode & PHYLINK_PCS_NEG_OUTBAND) + return "outband"; + + if (mode & PHYLINK_PCS_NEG_INBAND) { + if (mode & PHYLINK_PCS_NEG_ENABLED) + return "inband,an-enabled"; + else + return "inband,an-disabled"; + } + + return "unknown"; +} + static unsigned int phylink_interface_signal_rate(phy_interface_t interface) { switch (interface) { @@ -235,6 +259,7 @@ static int phylink_interface_max_speed(phy_interface_t interface) return SPEED_1000; case PHY_INTERFACE_MODE_2500BASEX: + case PHY_INTERFACE_MODE_10G_QXGMII: return SPEED_2500; case PHY_INTERFACE_MODE_5GBASER: @@ -504,7 +529,11 @@ static unsigned long phylink_get_capabilities(phy_interface_t interface, switch (interface) { case PHY_INTERFACE_MODE_USXGMII: - caps |= MAC_10000FD | MAC_5000FD | MAC_2500FD; + caps |= MAC_10000FD | MAC_5000FD; + fallthrough; + + case PHY_INTERFACE_MODE_10G_QXGMII: + caps |= MAC_2500FD; fallthrough; case PHY_INTERFACE_MODE_RGMII_TXID: @@ -598,15 +627,8 @@ static unsigned long phylink_get_capabilities(phy_interface_t interface, * max speed at full duplex. */ if (mac_capabilities & - phylink_cap_from_speed_duplex(max_speed, DUPLEX_FULL)) { - /* Although a duplex-matching phy might exist, we - * conservatively remove these modes because the MAC - * will not be aware of the half-duplex nature of the - * link. - */ + phylink_cap_from_speed_duplex(max_speed, DUPLEX_FULL)) matched_caps = GENMASK(__fls(caps), __fls(MAC_10HD)); - matched_caps &= ~(MAC_1000HD | MAC_100HD | MAC_10HD); - } break; } case RATE_MATCH_CRS: @@ -651,21 +673,19 @@ static void phylink_validate_mask_caps(unsigned long *supported, linkmode_and(state->advertising, state->advertising, mask); } -static int phylink_validate_mac_and_pcs(struct phylink *pl, unsigned int mode, +static int phylink_validate_mac_and_pcs(struct phylink *pl, unsigned long *supported, struct phylink_link_state *state) { + struct phylink_pcs *pcs = NULL; unsigned long capabilities; - struct phylink_pcs *pcs; int ret; /* Get the PCS for this interface mode */ - if (pl->using_mac_select_pcs) { + if (pl->mac_ops->mac_select_pcs) { pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface); if (IS_ERR(pcs)) return PTR_ERR(pcs); - } else { - pcs = pl->pcs; } if (pcs) { @@ -680,10 +700,20 @@ static int phylink_validate_mac_and_pcs(struct phylink *pl, unsigned int mode, return -EINVAL; } + /* Ensure that this PCS supports the interface which the MAC + * returned it for. It is an error for the MAC to return a PCS + * that does not support the interface mode. + */ + if (!phy_interface_empty(pcs->supported_interfaces) && + !test_bit(state->interface, pcs->supported_interfaces)) { + phylink_err(pl, "MAC returned PCS which does not support %s\n", + phy_modes(state->interface)); + return -EINVAL; + } + /* Validate the link parameters with the PCS */ if (pcs->ops->pcs_validate) { - ret = pcs->ops->pcs_validate(pcs, mode, supported, - state); + ret = pcs->ops->pcs_validate(pcs, supported, state); if (ret < 0 || phylink_is_empty_linkmode(supported)) return -EINVAL; @@ -708,7 +738,6 @@ static int phylink_validate_mac_and_pcs(struct phylink *pl, unsigned int mode, } static void phylink_validate_one(struct phylink *pl, struct phy_device *phy, - unsigned int mode, const unsigned long *supported, const struct phylink_link_state *state, phy_interface_t interface, @@ -726,8 +755,7 @@ static void phylink_validate_one(struct phylink *pl, struct phy_device *phy, if (phy) tmp_state.rate_matching = phy_get_rate_matching(phy, interface); - if (!phylink_validate_mac_and_pcs(pl, mode, tmp_supported, - &tmp_state)) { + if (!phylink_validate_mac_and_pcs(pl, tmp_supported, &tmp_state)) { phylink_dbg(pl, " interface %u (%s) rate match %s supports %*pbl\n", interface, phy_modes(interface), phy_rate_matching_to_str(tmp_state.rate_matching), @@ -740,7 +768,6 @@ static void phylink_validate_one(struct phylink *pl, struct phy_device *phy, } static int phylink_validate_mask(struct phylink *pl, struct phy_device *phy, - unsigned int mode, unsigned long *supported, struct phylink_link_state *state, const unsigned long *interfaces) @@ -750,7 +777,7 @@ static int phylink_validate_mask(struct phylink *pl, struct phy_device *phy, int interface; for_each_set_bit(interface, interfaces, PHY_INTERFACE_MODE_MAX) - phylink_validate_one(pl, phy, mode, supported, state, interface, + phylink_validate_one(pl, phy, supported, state, interface, all_s, all_adv); linkmode_copy(supported, all_s); @@ -759,27 +786,26 @@ static int phylink_validate_mask(struct phylink *pl, struct phy_device *phy, return phylink_is_empty_linkmode(supported) ? -EINVAL : 0; } -static int phylink_validate(struct phylink *pl, unsigned int mode, - unsigned long *supported, +static int phylink_validate(struct phylink *pl, unsigned long *supported, struct phylink_link_state *state) { const unsigned long *interfaces = pl->config->supported_interfaces; if (state->interface == PHY_INTERFACE_MODE_NA) - return phylink_validate_mask(pl, NULL, mode, supported, state, + return phylink_validate_mask(pl, NULL, supported, state, interfaces); if (!test_bit(state->interface, interfaces)) return -EINVAL; - return phylink_validate_mac_and_pcs(pl, mode, supported, state); + return phylink_validate_mac_and_pcs(pl, supported, state); } static int phylink_parse_fixedlink(struct phylink *pl, const struct fwnode_handle *fwnode) { + __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; struct fwnode_handle *fixed_node; - bool pause, asym_pause, autoneg; const struct phy_setting *s; struct gpio_desc *desc; u32 speed; @@ -850,24 +876,17 @@ static int phylink_parse_fixedlink(struct phylink *pl, linkmode_fill(pl->supported); linkmode_copy(pl->link_config.advertising, pl->supported); - phylink_validate(pl, MLO_AN_FIXED, pl->supported, &pl->link_config); + phylink_validate(pl, pl->supported, &pl->link_config); - pause = phylink_test(pl->supported, Pause); - asym_pause = phylink_test(pl->supported, Asym_Pause); - autoneg = phylink_test(pl->supported, Autoneg); s = phy_lookup_setting(pl->link_config.speed, pl->link_config.duplex, pl->supported, true); - linkmode_zero(pl->supported); - phylink_set(pl->supported, MII); - - if (pause) - phylink_set(pl->supported, Pause); - if (asym_pause) - phylink_set(pl->supported, Asym_Pause); + linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, mask); + linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, mask); + linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, mask); + linkmode_and(pl->supported, pl->supported, mask); - if (autoneg) - phylink_set(pl->supported, Autoneg); + phylink_set(pl->supported, MII); if (s) { __set_bit(s->bit, pl->supported); @@ -894,26 +913,31 @@ static int phylink_parse_mode(struct phylink *pl, const char *managed; unsigned long caps; + if (pl->config->default_an_inband) + pl->cfg_link_an_mode = MLO_AN_INBAND; + dn = fwnode_get_named_child_node(fwnode, "fixed-link"); if (dn || fwnode_property_present(fwnode, "fixed-link")) pl->cfg_link_an_mode = MLO_AN_FIXED; fwnode_handle_put(dn); if ((fwnode_property_read_string(fwnode, "managed", &managed) == 0 && - strcmp(managed, "in-band-status") == 0) || - pl->config->ovr_an_inband) { - if (phylink_mode_fixed(pl->cfg_link_an_mode)) { + strcmp(managed, "in-band-status") == 0)) { + if (pl->cfg_link_an_mode == MLO_AN_FIXED) { phylink_err(pl, "can't use both fixed-link and in-band-status\n"); return -EINVAL; } + pl->cfg_link_an_mode = MLO_AN_INBAND; + } + + if (pl->cfg_link_an_mode == MLO_AN_INBAND) { linkmode_zero(pl->supported); phylink_set(pl->supported, MII); phylink_set(pl->supported, Autoneg); phylink_set(pl->supported, Asym_Pause); phylink_set(pl->supported, Pause); - pl->cfg_link_an_mode = MLO_AN_INBAND; switch (pl->link_config.interface) { case PHY_INTERFACE_MODE_SGMII: @@ -930,6 +954,7 @@ static int phylink_parse_mode(struct phylink *pl, case PHY_INTERFACE_MODE_5GBASER: case PHY_INTERFACE_MODE_25GBASER: case PHY_INTERFACE_MODE_USXGMII: + case PHY_INTERFACE_MODE_10G_QXGMII: case PHY_INTERFACE_MODE_10GKR: case PHY_INTERFACE_MODE_10GBASER: case PHY_INTERFACE_MODE_XLGMII: @@ -948,8 +973,7 @@ static int phylink_parse_mode(struct phylink *pl, linkmode_copy(pl->link_config.advertising, pl->supported); - if (phylink_validate(pl, pl->cfg_link_an_mode, pl->supported, - &pl->link_config)) { + if (phylink_validate(pl, pl->supported, &pl->link_config)) { phylink_err(pl, "failed to validate link configuration for in-band status\n"); return -EINVAL; @@ -987,11 +1011,11 @@ static void phylink_resolve_an_pause(struct phylink_link_state *state) } } -static unsigned int phylink_pcs_query_inband(struct phylink_pcs *pcs, +static unsigned int phylink_pcs_inband_caps(struct phylink_pcs *pcs, phy_interface_t interface) { - if (pcs && pcs->ops->pcs_query_inband) - return pcs->ops->pcs_query_inband(pcs, interface); + if (pcs && pcs->ops->pcs_inband_caps) + return pcs->ops->pcs_inband_caps(pcs, interface); return 0; } @@ -1049,19 +1073,63 @@ static void phylink_pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode, pcs->ops->pcs_link_up(pcs, neg_mode, interface, speed, duplex); } +static void phylink_pcs_disable_eee(struct phylink_pcs *pcs) +{ + if (pcs && pcs->ops->pcs_disable_eee) + pcs->ops->pcs_disable_eee(pcs); +} + +static void phylink_pcs_enable_eee(struct phylink_pcs *pcs) +{ + if (pcs && pcs->ops->pcs_enable_eee) + pcs->ops->pcs_enable_eee(pcs); +} + +/* Query inband for a specific interface mode, asking the MAC for the + * PCS which will be used to handle the interface mode. + */ +static unsigned int phylink_inband_caps(struct phylink *pl, + phy_interface_t interface) +{ + struct phylink_pcs *pcs; + + if (!pl->mac_ops->mac_select_pcs) + return 0; + + pcs = pl->mac_ops->mac_select_pcs(pl->config, interface); + if (!pcs) + return 0; + + return phylink_pcs_inband_caps(pcs, interface); +} + static void phylink_pcs_poll_stop(struct phylink *pl) { - if (phylink_mode_inband(pl->cfg_link_an_mode)) + if (pl->cfg_link_an_mode == MLO_AN_INBAND) del_timer(&pl->link_poll); } static void phylink_pcs_poll_start(struct phylink *pl) { - if (pl->pcs && pl->pcs->poll && - phylink_mode_inband(pl->cfg_link_an_mode)) + if (pl->pcs && pl->pcs->poll && pl->cfg_link_an_mode == MLO_AN_INBAND) mod_timer(&pl->link_poll, jiffies + HZ); } +int phylink_pcs_pre_init(struct phylink *pl, struct phylink_pcs *pcs) +{ + int ret = 0; + + /* Signal to PCS driver that MAC requires RX clock for init */ + if (pl->config->mac_requires_rxc) + pcs->rxc_always_on = true; + + if (pcs->ops->pcs_pre_init) + ret = pcs->ops->pcs_pre_init(pcs); + + return ret; +} +EXPORT_SYMBOL_GPL(phylink_pcs_pre_init); + static void phylink_mac_config(struct phylink *pl, const struct phylink_link_state *state) { @@ -1076,13 +1144,13 @@ static void phylink_mac_config(struct phylink *pl, phylink_dbg(pl, "%s: mode=%s/%s/%s adv=%*pb pause=%02x\n", - __func__, phylink_an_mode_str(pl->cur_link_an_mode), + __func__, phylink_an_mode_str(pl->act_link_an_mode), phy_modes(st.interface), phy_rate_matching_to_str(st.rate_matching), __ETHTOOL_LINK_MODE_MASK_NBITS, st.advertising, st.pause); - pl->mac_ops->mac_config(pl->config, pl->cur_link_an_mode, &st); + pl->mac_ops->mac_config(pl->config, pl->act_link_an_mode, &st); } static void phylink_pcs_an_restart(struct phylink *pl) @@ -1090,7 +1158,7 @@ static void phylink_pcs_an_restart(struct phylink *pl) if (pl->pcs && linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, pl->link_config.advertising) && phy_interface_mode_is_8023z(pl->link_config.interface) && - phylink_autoneg_inband(pl->cur_link_an_mode)) + phylink_autoneg_inband(pl->act_link_an_mode)) pl->pcs->ops->pcs_an_restart(pl->pcs); } @@ -1098,7 +1166,6 @@ static void phylink_pcs_an_restart(struct phylink *pl) * phylink_pcs_neg_mode() - helper to determine PCS inband mode * @pl: a pointer to a &struct phylink returned from phylink_create() * @pcs: a pointer to &struct phylink_pcs - * @mode: one of %MLO_AN_FIXED, %MLO_AN_PHY, %MLO_AN_INBAND. * @interface: interface mode to be used * @advertising: adertisement ethtool link mode mask * @@ -1115,25 +1182,28 @@ static void phylink_pcs_an_restart(struct phylink *pl) * Note: this is for cases where the PCS itself is involved in negotiation * (e.g. Clause 37, SGMII and similar) not Clause 73. */ -static unsigned int phylink_pcs_neg_mode(struct phylink *pl, - struct phylink_pcs *pcs, - unsigned int mode, - phy_interface_t interface, - const unsigned long *advertising) +static void phylink_pcs_neg_mode(struct phylink *pl, struct phylink_pcs *pcs, + phy_interface_t interface, + const unsigned long *advertising) { - unsigned int phy_link_mode = 0; - unsigned int pcs_link_mode; - unsigned int neg_mode; + unsigned int pcs_ib_caps = 0; + unsigned int phy_ib_caps = 0; + unsigned int neg_mode, mode; enum { INBAND_CISCO_SGMII, - INBAND_8023Z, + INBAND_BASEX, } type; + mode = pl->req_link_an_mode; + + pl->phy_ib_mode = 0; + switch (interface) { case PHY_INTERFACE_MODE_SGMII: case PHY_INTERFACE_MODE_QSGMII: case PHY_INTERFACE_MODE_QUSGMII: case PHY_INTERFACE_MODE_USXGMII: + case PHY_INTERFACE_MODE_10G_QXGMII: /* These protocols are designed for use with a PHY which * communicates its negotiation result back to the MAC via * inband communication. Note: there exist PHYs that run @@ -1150,93 +1220,143 @@ static unsigned int phylink_pcs_neg_mode(struct phylink *pl, * as well, but drivers may not support this, so may * need to override this. */ - type = INBAND_8023Z; + type = INBAND_BASEX; break; default: - return PHYLINK_PCS_NEG_NONE; + pl->pcs_neg_mode = PHYLINK_PCS_NEG_NONE; + pl->act_link_an_mode = mode; + return; } - pcs_link_mode = phylink_pcs_query_inband(pcs, interface); + if (pcs) + pcs_ib_caps = phylink_pcs_inband_caps(pcs, interface); + if (pl->phydev) - phy_link_mode = phy_query_inband(pl->phydev, interface); + phy_ib_caps = phy_inband_caps(pl->phydev, interface); + + phylink_dbg(pl, "interface %s inband modes: pcs=%02x phy=%02x\n", + phy_modes(interface), pcs_ib_caps, phy_ib_caps); if (!phylink_autoneg_inband(mode)) { + bool pcs_ib_only = false; + bool phy_ib_only = false; + + if (pcs_ib_caps && pcs_ib_caps != LINK_INBAND_DISABLE) { + /* PCS supports reporting in-band capabilities, and + * supports more than disable mode. + */ + if (pcs_ib_caps & LINK_INBAND_DISABLE) + neg_mode = PHYLINK_PCS_NEG_OUTBAND; + else if (pcs_ib_caps & LINK_INBAND_ENABLE) + pcs_ib_only = true; + } + + if (phy_ib_caps && phy_ib_caps != LINK_INBAND_DISABLE) { + /* PHY supports in-band capabilities, and supports + * more than disable mode. + */ + if (phy_ib_caps & LINK_INBAND_DISABLE) + pl->phy_ib_mode = LINK_INBAND_DISABLE; + else if (phy_ib_caps & LINK_INBAND_BYPASS) + pl->phy_ib_mode = LINK_INBAND_BYPASS; + else if (phy_ib_caps & LINK_INBAND_ENABLE) + phy_ib_only = true; + } + /* If either the PCS or PHY requires inband to be enabled, * this is an invalid configuration. Provide a diagnostic * message for this case, but don't try to force the issue. */ - if ((pcs_link_mode | phy_link_mode) & LINK_INBAND_REQUIRED) { - const char *s1, *s2, *s3, *empty = ""; - - s1 = s2 = s3 = empty; - if (pcs_link_mode & LINK_INBAND_REQUIRED) - s1 = "PCS"; - - if (phy_link_mode & LINK_INBAND_REQUIRED) - s3 = "PHY"; - - if (s1 != empty && s3 != empty) - s2 = " and "; - + if (pcs_ib_only || phy_ib_only) phylink_warn(pl, "firmware wants %s mode, but %s%s%s requires inband\n", phylink_an_mode_str(mode), - s1, s2, s3); - } - return PHYLINK_PCS_NEG_OUTBAND; - } - - /* For SGMII modes, which are designed to be used with PHYs, we - * try to use inband mode where-ever possible. - */ - if (type == INBAND_CISCO_SGMII) { - /* If the PCS or PHY can not provide inband, then use - * out of band. - */ - if (pcs_link_mode == LINK_INBAND_VALID || - phy_link_mode == LINK_INBAND_VALID) { - /* If either require inband, then we have a conflict. */ - return PHYLINK_PCS_NEG_OUTBAND; - } - - /* Otherwise, use inband as this is what was requested. - * This covers the case where the supported inband modes - * have not been provided, or the PCS and PHY report that - * they support inband. - */ - return PHYLINK_PCS_NEG_INBAND_ENABLED; - } - - /* For 802.3z, handle the PHY present vs absent cases separately */ - if (!pl->phydev) { - /* PHY absent: always use inband but inband may be disabled */ - /* If the PCS doesn't support inband, then inband must be - * disabled. + pcs_ib_only ? "PCS" : "", + pcs_ib_only && phy_ib_only ? " and " : "", + phy_ib_only ? "PHY" : ""); + + neg_mode = PHYLINK_PCS_NEG_OUTBAND; + } else if (type == INBAND_CISCO_SGMII || pl->phydev) { + /* For SGMII modes which are designed to be used with PHYs, or + * Base-X with a PHY, we try to use in-band mode where-ever + * possible. However, there are some PHYs e.g. BCM84881 which + * do not support in-band. */ - if (pcs_link_mode == LINK_INBAND_VALID) - return PHYLINK_PCS_NEG_INBAND_DISABLED; - - /* If the PCS requires inband, then inband must always be - * enabled. + const unsigned int inband_ok = LINK_INBAND_ENABLE | + LINK_INBAND_BYPASS; + const unsigned int outband_ok = LINK_INBAND_DISABLE | + LINK_INBAND_BYPASS; + /* PCS PHY + * D E D E + * 0 0 0 0 no information inband enabled + * 1 0 0 0 pcs doesn't support outband + * 0 1 0 0 pcs required inband enabled + * 1 1 0 0 pcs optional inband enabled + * 0 0 1 0 phy doesn't support outband + * 1 0 1 0 pcs+phy doesn't support outband + * 0 1 1 0 pcs required, phy doesn't support, invalid + * 1 1 1 0 pcs optional, phy doesn't support, outband + * 0 0 0 1 phy required inband enabled + * 1 0 0 1 pcs doesn't support, phy required, invalid + * 0 1 0 1 pcs+phy required inband enabled + * 1 1 0 1 pcs optional, phy required inband enabled + * 0 0 1 1 phy optional inband enabled + * 1 0 1 1 pcs doesn't support, phy optional, outband + * 0 1 1 1 pcs required, phy optional inband enabled + * 1 1 1 1 pcs+phy optional inband enabled */ - if (pcs_link_mode & LINK_INBAND_REQUIRED) - return PHYLINK_PCS_NEG_INBAND_ENABLED; + if ((!pcs_ib_caps || pcs_ib_caps & inband_ok) && + (!phy_ib_caps || phy_ib_caps & inband_ok)) { + /* In-band supported or unknown at both ends. Enable + * in-band mode with or without bypass at the PHY. + */ + if (phy_ib_caps & LINK_INBAND_ENABLE) + pl->phy_ib_mode = LINK_INBAND_ENABLE; + else if (phy_ib_caps & LINK_INBAND_BYPASS) + pl->phy_ib_mode = LINK_INBAND_BYPASS; + + neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED; + } else if ((!pcs_ib_caps || pcs_ib_caps & outband_ok) && + (!phy_ib_caps || phy_ib_caps & outband_ok)) { + /* Either in-band not supported at at least one end. + * In-band bypass at the other end is possible. + */ + if (phy_ib_caps & LINK_INBAND_DISABLE) + pl->phy_ib_mode = LINK_INBAND_DISABLE; + else if (phy_ib_caps & LINK_INBAND_BYPASS) + pl->phy_ib_mode = LINK_INBAND_BYPASS; - /* For the possible case, fall through to the "legacy" code. */ + neg_mode = PHYLINK_PCS_NEG_OUTBAND; + if (pl->phydev) + mode = MLO_AN_PHY; + } else { + /* invalid */ + phylink_warn(pl, "%s: incompatible in-band capabilities, trying in-band", + phy_modes(interface)); + neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED; + } } else { - /* PHY present, inband mode depends on the capabilities - * of both. - */ + /* For Base-X without a PHY */ + if (pcs_ib_caps == LINK_INBAND_DISABLE) + /* If the PCS doesn't support inband, then inband must + * be disabled. + */ + neg_mode = PHYLINK_PCS_NEG_INBAND_DISABLED; + else if (pcs_ib_caps == LINK_INBAND_ENABLE) + /* If the PCS requires inband, then inband must always + * be enabled. + */ + neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED; + else if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, + advertising)) + neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED; + else + neg_mode = PHYLINK_PCS_NEG_INBAND_DISABLED; } - /* Legacy, so determine inband depending on the advertising bit */ - if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, advertising)) - neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED; - else - neg_mode = PHYLINK_PCS_NEG_INBAND_DISABLED; - - return neg_mode; + pl->pcs_neg_mode = neg_mode; + pl->act_link_an_mode = mode; } static void phylink_major_config(struct phylink *pl, bool restart, @@ -1248,9 +1368,11 @@ static void phylink_major_config(struct phylink *pl, bool restart, unsigned int neg_mode; int err; - phylink_dbg(pl, "major config %s\n", phy_modes(state->interface)); + phylink_dbg(pl, "major config, requested %s/%s\n", + phylink_an_mode_str(pl->req_link_an_mode), + phy_modes(state->interface)); - if (pl->using_mac_select_pcs) { + if (pl->mac_ops->mac_select_pcs) { pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface); if (IS_ERR(pcs)) { phylink_err(pl, @@ -1259,18 +1381,20 @@ static void phylink_major_config(struct phylink *pl, bool restart, return; } - pcs_changed = pcs && pl->pcs != pcs; + pcs_changed = pl->pcs != pcs; } - pl->pcs_neg_mode = phylink_pcs_neg_mode(pl, pcs, - pl->cur_link_an_mode, - state->interface, - state->advertising); + phylink_pcs_neg_mode(pl, pcs, state->interface, state->advertising); + + phylink_dbg(pl, "major config, active %s/%s/%s\n", + phylink_an_mode_str(pl->act_link_an_mode), + phylink_pcs_mode_str(pl->pcs_neg_mode), + phy_modes(state->interface)); phylink_pcs_poll_stop(pl); if (pl->mac_ops->mac_prepare) { - err = pl->mac_ops->mac_prepare(pl->config, pl->cur_link_an_mode, + err = pl->mac_ops->mac_prepare(pl->config, pl->act_link_an_mode, state->interface); if (err < 0) { phylink_err(pl, "mac_prepare failed: %pe\n", @@ -1304,7 +1428,7 @@ static void phylink_major_config(struct phylink *pl, bool restart, if (pl->pcs_state == PCS_STATE_STARTING || pcs_changed) phylink_pcs_enable(pl->pcs); - neg_mode = pl->cur_link_an_mode; + neg_mode = pl->act_link_an_mode; if (pl->pcs && pl->pcs->neg_mode) neg_mode = pl->pcs_neg_mode; @@ -1320,13 +1444,20 @@ static void phylink_major_config(struct phylink *pl, bool restart, phylink_pcs_an_restart(pl); if (pl->mac_ops->mac_finish) { - err = pl->mac_ops->mac_finish(pl->config, pl->cur_link_an_mode, + err = pl->mac_ops->mac_finish(pl->config, pl->act_link_an_mode, state->interface); if (err < 0) phylink_err(pl, "mac_finish failed: %pe\n", ERR_PTR(err)); } + if (pl->phydev && pl->phy_ib_mode) { + err = phy_config_inband(pl->phydev, pl->phy_ib_mode); + if (err < 0) + phylink_err(pl, "phy_config_inband: %pe\n", + ERR_PTR(err)); + } + if (pl->sfp_bus) { rate_kbd = phylink_interface_signal_rate(state->interface); if (rate_kbd) @@ -1351,18 +1482,16 @@ static int phylink_change_inband_advert(struct phylink *pl) return 0; phylink_dbg(pl, "%s: mode=%s/%s adv=%*pb pause=%02x\n", __func__, - phylink_an_mode_str(pl->cur_link_an_mode), + phylink_an_mode_str(pl->req_link_an_mode), phy_modes(pl->link_config.interface), __ETHTOOL_LINK_MODE_MASK_NBITS, pl->link_config.advertising, pl->link_config.pause); /* Recompute the PCS neg mode */ - pl->pcs_neg_mode = phylink_pcs_neg_mode(pl, pl->pcs, - pl->cur_link_an_mode, - pl->link_config.interface, - pl->link_config.advertising); + phylink_pcs_neg_mode(pl, pl->pcs, pl->link_config.interface, + pl->link_config.advertising); - neg_mode = pl->cur_link_an_mode; + neg_mode = pl->act_link_an_mode; if (pl->pcs->neg_mode) neg_mode = pl->pcs_neg_mode; @@ -1384,12 +1513,24 @@ static int phylink_change_inband_advert(struct phylink *pl) static void phylink_mac_pcs_get_state(struct phylink *pl, struct phylink_link_state *state) { + struct phylink_pcs *pcs; + bool autoneg; + linkmode_copy(state->advertising, pl->link_config.advertising); linkmode_zero(state->lp_advertising); state->interface = pl->link_config.interface; state->rate_matching = pl->link_config.rate_matching; - if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, - state->advertising)) { + state->an_complete = 0; + state->link = 1; + + pcs = pl->pcs; + if (!pcs || pcs->neg_mode) + autoneg = pl->pcs_neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED; + else + autoneg = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, + state->advertising); + + if (autoneg) { state->speed = SPEED_UNKNOWN; state->duplex = DUPLEX_UNKNOWN; state->pause = MLO_PAUSE_NONE; @@ -1398,11 +1539,9 @@ static void phylink_mac_pcs_get_state(struct phylink *pl, state->duplex = pl->link_config.duplex; state->pause = pl->link_config.pause; } - state->an_complete = 0; - state->link = 1; - if (pl->pcs) - pl->pcs->ops->pcs_get_state(pl->pcs, state); + if (pcs) + pcs->ops->pcs_get_state(pcs, pl->pcs_neg_mode, state); else state->link = 0; } @@ -1427,7 +1566,7 @@ static void phylink_mac_initial_config(struct phylink *pl, bool force_restart) { struct phylink_link_state link_state; - switch (pl->cur_link_an_mode) { + switch (pl->req_link_an_mode) { case MLO_AN_PHY: link_state = pl->phy_state; break; @@ -1466,73 +1605,49 @@ static const char *phylink_pause_to_str(int pause) } } -static void phylink_disable_tx_lpi(struct phylink *pl) +static void phylink_deactivate_lpi(struct phylink *pl) { - phylink_dbg(pl, "disabling tx_lpi\n"); + if (pl->mac_enable_tx_lpi) { + pl->mac_enable_tx_lpi = false; - if (pl->mac_ops->mac_disable_tx_lpi) - pl->mac_ops->mac_disable_tx_lpi(pl->config); -} - -static void phylink_enable_tx_lpi(struct phylink *pl) -{ - phylink_dbg(pl, "enabling tx_lpi, timer %uus\n", - pl->eee_cfg.tx_lpi_timer); - - if (pl->mac_ops->mac_enable_tx_lpi) - pl->mac_ops->mac_enable_tx_lpi(pl->config, - pl->eee_cfg.tx_lpi_timer); -} + phylink_dbg(pl, "disabling LPI\n"); -static bool phylink_eee_is_active(struct phylink *pl) -{ - return phylink_init_eee(pl, pl->config->eee_clk_stop_enable) >= 0; -} + pl->mac_ops->mac_disable_tx_lpi(pl->config, pl->cur_interface); -static void phylink_deactivate_eee(struct phylink *pl) -{ - phylink_dbg(pl, "deactivating EEE, was %sactive\n", - pl->eee_active ? "" : "in"); - - if (pl->eee_active) { - pl->eee_active = false; - phylink_disable_tx_lpi(pl); + phylink_pcs_disable_eee(pl->pcs); } } -static void phylink_activate_eee(struct phylink *pl) +static void phylink_activate_lpi(struct phylink *pl) { - pl->eee_active = phylink_eee_is_active(pl); - - phylink_dbg(pl, "can LPI, EEE enabled, %sactive\n", - pl->eee_active ? "" : "in"); - - if (pl->eee_active) - phylink_enable_tx_lpi(pl); -} + int err; -/* Determine whether the MAC has new EEE support. We detect this by checking - * for the two new methods being present, but for DSA it will populate these - * anyway, so also check that lpi_capabilities is non-zero. - */ -static bool phylink_mac_supports_eee(struct phylink *pl) -{ - return pl->mac_ops->mac_disable_tx_lpi && - pl->mac_ops->mac_enable_tx_lpi && - pl->config->lpi_capabilities; -} + if (!test_bit(pl->cur_interface, pl->config->lpi_interfaces)) { + phylink_dbg(pl, "MAC does not support LPI with %s\n", + phy_modes(pl->cur_interface)); + return; + } -static void phylink_phy_restrict_eee(struct phylink *pl, struct phy_device *phy) -{ - __ETHTOOL_DECLARE_LINK_MODE_MASK(eee_supported); + phylink_dbg(pl, "LPI timer %uus, tx clock stop %u\n", + pl->mac_tx_lpi_timer, pl->mac_tx_clk_stop); - /* Convert the MAC's LPI capabilities to linkmodes */ - linkmode_zero(eee_supported); - phylink_caps_to_linkmodes(eee_supported, pl->config->lpi_capabilities); + phylink_pcs_enable_eee(pl->pcs); - /* Mask out EEE modes that are not supported */ - linkmode_and(phy->supported_eee, phy->supported_eee, eee_supported); - linkmode_and(phy->advertising_eee, phy->advertising_eee, eee_supported); + err = pl->mac_ops->mac_enable_tx_lpi(pl->config, pl->cur_interface, + pl->mac_tx_lpi_timer, + pl->mac_tx_clk_stop); + if (!err) { + pl->mac_enable_tx_lpi = true; + } else { + phylink_pcs_disable_eee(pl->pcs); + if (err == -EOPNOTSUPP) + phylink_dbg(pl, "%ps doesn't support LPI", + pl->mac_ops->mac_enable_tx_lpi); + else + phylink_err(pl, "%ps() failed: %pe\n", + pl->mac_ops->mac_enable_tx_lpi, + ERR_PTR(err)); + } } static void phylink_link_up(struct phylink *pl, @@ -1570,19 +1685,19 @@ static void phylink_link_up(struct phylink *pl, pl->cur_interface = link_state.interface; - neg_mode = pl->cur_link_an_mode; + neg_mode = pl->act_link_an_mode; if (pl->pcs && pl->pcs->neg_mode) neg_mode = pl->pcs_neg_mode; phylink_pcs_link_up(pl->pcs, neg_mode, pl->cur_interface, speed, duplex); - pl->mac_ops->mac_link_up(pl->config, pl->phydev, pl->cur_link_an_mode, + pl->mac_ops->mac_link_up(pl->config, pl->phydev, pl->act_link_an_mode, pl->cur_interface, speed, duplex, !!(link_state.pause & MLO_PAUSE_TX), rx_pause); - if (eeecfg_mac_can_tx_lpi(&pl->eee_cfg)) - phylink_activate_eee(pl); + if (pl->mac_supports_eee && pl->phy_enable_tx_lpi) + phylink_activate_lpi(pl); if (ndev) netif_carrier_on(ndev); @@ -1601,9 +1716,9 @@ static void phylink_link_down(struct phylink *pl) if (ndev) netif_carrier_off(ndev); - phylink_deactivate_eee(pl); + phylink_deactivate_lpi(pl); - pl->mac_ops->mac_link_down(pl->config, pl->cur_link_an_mode, + pl->mac_ops->mac_link_down(pl->config, pl->act_link_an_mode, pl->cur_interface); phylink_info(pl, "Link is Down\n"); } @@ -1625,81 +1740,71 @@ static void phylink_resolve(struct work_struct *w) cur_link_state = phylink_link_is_up(pl); if (pl->phylink_disable_state) { - pl->mac_link_dropped = false; + pl->link_failed = false; link_state.link = false; - } else if (pl->mac_link_dropped) { + } else if (pl->link_failed) { link_state.link = false; retrigger = true; + } else if (pl->act_link_an_mode == MLO_AN_FIXED) { + phylink_get_fixed_state(pl, &link_state); + mac_config = link_state.link; + } else if (pl->act_link_an_mode == MLO_AN_PHY) { + link_state = pl->phy_state; + mac_config = link_state.link; } else { - switch (pl->cur_link_an_mode) { - case MLO_AN_PHY: - link_state = pl->phy_state; - phylink_apply_manual_flow(pl, &link_state); - mac_config = link_state.link; - break; + phylink_mac_pcs_get_state(pl, &link_state); - case MLO_AN_FIXED: - phylink_get_fixed_state(pl, &link_state); - mac_config = link_state.link; - break; + /* The PCS may have a latching link-fail indicator. If the link + * was up, bring the link down and re-trigger the resolve. + * Otherwise, re-read the PCS state to get the current status + * of the link. + */ + if (!link_state.link) { + if (cur_link_state) + retrigger = true; + else + phylink_mac_pcs_get_state(pl, &link_state); + } - case MLO_AN_INBAND: - phylink_mac_pcs_get_state(pl, &link_state); + /* If we have a phy, the "up" state is the union of both the + * PHY and the MAC + */ + if (pl->phydev) + link_state.link &= pl->phy_state.link; - /* The PCS may have a latching link-fail indicator. - * If the link was up, bring the link down and - * re-trigger the resolve. Otherwise, re-read the - * PCS state to get the current status of the link. + /* Only update if the PHY link is up */ + if (pl->phydev && pl->phy_state.link) { + /* If the interface has changed, force a link down + * event if the link isn't already down, and re-resolve. */ - if (!link_state.link) { - if (cur_link_state) - retrigger = true; - else - phylink_mac_pcs_get_state(pl, - &link_state); + if (link_state.interface != pl->phy_state.interface) { + retrigger = true; + link_state.link = false; } - /* If we have a phy, the "up" state is the union of - * both the PHY and the MAC + link_state.interface = pl->phy_state.interface; + + /* If we are doing rate matching, then the link + * speed/duplex comes from the PHY */ - if (pl->phydev) - link_state.link &= pl->phy_state.link; - - /* Only update if the PHY link is up */ - if (pl->phydev && pl->phy_state.link) { - /* If the interface has changed, force a - * link down event if the link isn't already - * down, and re-resolve. - */ - if (link_state.interface != - pl->phy_state.interface) { - retrigger = true; - link_state.link = false; - } - link_state.interface = pl->phy_state.interface; - - /* If we are doing rate matching, then the - * link speed/duplex comes from the PHY - */ - if (pl->phy_state.rate_matching) { - link_state.rate_matching = - pl->phy_state.rate_matching; - link_state.speed = pl->phy_state.speed; - link_state.duplex = - pl->phy_state.duplex; - } - - /* If we have a PHY, we need to update with - * the PHY flow control bits. - */ - link_state.pause = pl->phy_state.pause; - mac_config = true; + if (pl->phy_state.rate_matching) { + link_state.rate_matching = + pl->phy_state.rate_matching; + link_state.speed = pl->phy_state.speed; + link_state.duplex = pl->phy_state.duplex; } - phylink_apply_manual_flow(pl, &link_state); - break; + + /* If we have a PHY, we need to update with the PHY + * flow control bits. + */ + link_state.pause = pl->phy_state.pause; + mac_config = true; } } + 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 @@ -1722,7 +1827,7 @@ static void phylink_resolve(struct work_struct *w) phylink_link_up(pl, link_state); } if (!link_state.link && retrigger) { - pl->mac_link_dropped = false; + pl->link_failed = false; queue_work(system_power_efficient_wq, &pl->resolve); } mutex_unlock(&pl->state_mutex); @@ -1786,6 +1891,48 @@ static int phylink_register_sfp(struct phylink *pl, } /** + * phylink_set_fixed_link() - set the fixed link + * @pl: a pointer to a &struct phylink returned from phylink_create() + * @state: a pointer to a struct phylink_link_state. + * + * This function is used when the link parameters are known and do not change, + * making it suitable for certain types of network connections. + * + * Returns: zero on success or negative error code. + */ +int phylink_set_fixed_link(struct phylink *pl, + const struct phylink_link_state *state) +{ + const struct phy_setting *s; + unsigned long *adv; + + if (pl->cfg_link_an_mode != MLO_AN_PHY || !state || + !test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) + return -EINVAL; + + s = phy_lookup_setting(state->speed, state->duplex, + pl->supported, true); + if (!s) + return -EINVAL; + + adv = pl->link_config.advertising; + linkmode_zero(adv); + linkmode_set_bit(s->bit, adv); + linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, adv); + + pl->link_config.speed = state->speed; + pl->link_config.duplex = state->duplex; + pl->link_config.link = 1; + pl->link_config.an_complete = 1; + + pl->cfg_link_an_mode = MLO_AN_FIXED; + pl->req_link_an_mode = pl->cfg_link_an_mode; + + return 0; +} +EXPORT_SYMBOL_GPL(phylink_set_fixed_link); + +/** * phylink_create() - create a phylink instance * @config: a pointer to the target &struct phylink_config * @fwnode: a pointer to a &struct fwnode_handle describing the network @@ -1806,7 +1953,6 @@ struct phylink *phylink_create(struct phylink_config *config, phy_interface_t iface, const struct phylink_mac_ops *mac_ops) { - bool using_mac_select_pcs = false; struct phylink *pl; int ret; @@ -1817,11 +1963,6 @@ struct phylink *phylink_create(struct phylink_config *config, return ERR_PTR(-EINVAL); } - if (mac_ops->mac_select_pcs && - mac_ops->mac_select_pcs(config, PHY_INTERFACE_MODE_NA) != - ERR_PTR(-EOPNOTSUPP)) - using_mac_select_pcs = true; - pl = kzalloc(sizeof(*pl), GFP_KERNEL); if (!pl) return ERR_PTR(-ENOMEM); @@ -1840,7 +1981,16 @@ struct phylink *phylink_create(struct phylink_config *config, return ERR_PTR(-EINVAL); } - pl->using_mac_select_pcs = using_mac_select_pcs; + pl->mac_supports_eee_ops = phylink_mac_implements_lpi(mac_ops); + pl->mac_supports_eee = pl->mac_supports_eee_ops && + pl->config->lpi_capabilities && + !phy_interface_empty(pl->config->lpi_interfaces); + + /* Set the default EEE configuration */ + pl->eee_cfg.eee_enabled = pl->config->eee_enabled_default; + pl->eee_cfg.tx_lpi_enabled = pl->eee_cfg.eee_enabled; + pl->eee_cfg.tx_lpi_timer = pl->config->lpi_timer_default; + pl->phy_state.interface = iface; pl->link_interface = iface; if (iface == PHY_INTERFACE_MODE_MOCA) @@ -1858,10 +2008,7 @@ struct phylink *phylink_create(struct phylink_config *config, linkmode_fill(pl->supported); linkmode_copy(pl->link_config.advertising, pl->supported); - phylink_validate(pl, MLO_AN_FIXED, pl->supported, &pl->link_config); - - /* Set the default EEE configuration */ - pl->eee_cfg = pl->config->eee; + phylink_validate(pl, pl->supported, &pl->link_config); ret = phylink_parse_mode(pl, fwnode); if (ret < 0) { @@ -1869,7 +2016,7 @@ struct phylink *phylink_create(struct phylink_config *config, return ERR_PTR(ret); } - if (phylink_mode_fixed(pl->cfg_link_an_mode)) { + if (pl->cfg_link_an_mode == MLO_AN_FIXED) { ret = phylink_parse_fixedlink(pl, fwnode); if (ret < 0) { kfree(pl); @@ -1877,7 +2024,7 @@ struct phylink *phylink_create(struct phylink_config *config, } } - pl->cur_link_an_mode = pl->cfg_link_an_mode; + pl->req_link_an_mode = pl->cfg_link_an_mode; ret = phylink_register_sfp(pl, fwnode); if (ret < 0) { @@ -1946,16 +2093,24 @@ static void phylink_phy_change(struct phy_device *phydev, bool up) pl->phy_state.pause |= MLO_PAUSE_RX; pl->phy_state.interface = phydev->interface; pl->phy_state.link = up; + if (!up) + pl->link_failed = true; + + /* Get the LPI state from phylib */ + pl->phy_enable_tx_lpi = phydev->enable_tx_lpi; + pl->mac_tx_lpi_timer = phydev->eee_cfg.tx_lpi_timer; mutex_unlock(&pl->state_mutex); phylink_run_resolve(pl); - phylink_dbg(pl, "phy link %s %s/%s/%s/%s/%s\n", up ? "up" : "down", + phylink_dbg(pl, "phy link %s %s/%s/%s/%s/%s/%slpi\n", + up ? "up" : "down", phy_modes(phydev->interface), phy_speed_to_str(phydev->speed), phy_duplex_to_str(phydev->duplex), phy_rate_matching_to_str(phydev->rate_matching), - phylink_pause_to_str(pl->phy_state.pause)); + phylink_pause_to_str(pl->phy_state.pause), + phydev->enable_tx_lpi ? "" : "no"); } static int phylink_validate_phy(struct phylink *pl, struct phy_device *phy, @@ -1963,9 +2118,6 @@ static int phylink_validate_phy(struct phylink *pl, struct phy_device *phy, struct phylink_link_state *state) { DECLARE_PHY_INTERFACE_MASK(interfaces); - unsigned int mode; - - mode = pl->cfg_link_an_mode; /* If the PHY provides a bitmap of the interfaces it will be using * depending on the negotiated media speeds, use this to validate @@ -2002,7 +2154,7 @@ static int phylink_validate_phy(struct phylink *pl, struct phy_device *phy, phy->possible_interfaces, (int)PHY_INTERFACE_MODE_MAX, interfaces); - return phylink_validate_mask(pl, phy, mode, supported, state, + return phylink_validate_mask(pl, phy, supported, state, interfaces); } @@ -2031,7 +2183,7 @@ static int phylink_validate_phy(struct phylink *pl, struct phy_device *phy, state->interface != PHY_INTERFACE_MODE_USXGMII) state->interface = PHY_INTERFACE_MODE_NA; - return phylink_validate(pl, mode, supported, state); + return phylink_validate(pl, supported, state); } static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy, @@ -2092,8 +2244,31 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy, /* If the MAC supports phylink managed EEE, restrict the EEE * advertisement according to the MAC's LPI capabilities. */ - if (phylink_mac_supports_eee(pl)) - phylink_phy_restrict_eee(pl, phy); + if (pl->mac_supports_eee) { + /* If EEE is enabled, then we need to call phy_support_eee() + * to ensure that the advertising mask is appropriately set. + * This also enables EEE at the PHY. + */ + if (pl->eee_cfg.eee_enabled) + phy_support_eee(phy); + + phy->eee_cfg.tx_lpi_enabled = pl->eee_cfg.tx_lpi_enabled; + phy->eee_cfg.tx_lpi_timer = pl->eee_cfg.tx_lpi_timer; + + /* Convert the MAC's LPI capabilities to linkmodes */ + linkmode_zero(pl->supported_lpi); + phylink_caps_to_linkmodes(pl->supported_lpi, + pl->config->lpi_capabilities); + + /* Restrict the PHYs EEE support/advertisement to the modes + * that the MAC supports. + */ + linkmode_and(phy->advertising_eee, phy->advertising_eee, + pl->supported_lpi); + } else if (pl->mac_supports_eee_ops) { + /* MAC supports phylink EEE, but wants EEE always disabled. */ + phy_disable_eee(phy); + } mutex_unlock(&pl->state_mutex); mutex_unlock(&phy->lock); @@ -2110,21 +2285,39 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy, if (pl->config->mac_managed_pm) phy->mac_managed_pm = true; - return 0; + /* Allow the MAC to stop its clock if the PHY has the capability */ + pl->mac_tx_clk_stop = phy_eee_tx_clock_stop_capable(phy) > 0; + + if (pl->mac_supports_eee_ops) { + /* Explicitly configure whether the PHY is allowed to stop its + * receive clock. + */ + ret = phy_eee_rx_clock_stop(phy, + pl->config->eee_rx_clk_stop_enable); + if (ret == -EOPNOTSUPP) + ret = 0; + } + + return ret; } static int phylink_attach_phy(struct phylink *pl, struct phy_device *phy, phy_interface_t interface) { - if (WARN_ON(phylink_mode_fixed(pl->cfg_link_an_mode) || - (phylink_mode_inband(pl->cfg_link_an_mode) && + u32 flags = 0; + + if (WARN_ON(pl->cfg_link_an_mode == MLO_AN_FIXED || + (pl->cfg_link_an_mode == MLO_AN_INBAND && phy_interface_mode_is_8023z(interface) && !pl->sfp_bus))) return -EINVAL; if (pl->phydev) return -EBUSY; - return phy_attach_direct(pl->netdev, phy, 0, interface); + if (pl->config->mac_requires_rxc) + flags |= PHY_F_RXC_ALWAYS_ON; + + return phy_attach_direct(pl->netdev, phy, flags, interface); } /** @@ -2203,14 +2396,14 @@ int phylink_fwnode_phy_connect(struct phylink *pl, int ret; /* Fixed links and 802.3z are handled without needing a PHY */ - if (phylink_mode_fixed(pl->cfg_link_an_mode) || - (phylink_mode_inband(pl->cfg_link_an_mode) && + if (pl->cfg_link_an_mode == MLO_AN_FIXED || + (pl->cfg_link_an_mode == MLO_AN_INBAND && phy_interface_mode_is_8023z(pl->link_interface))) return 0; phy_fwnode = fwnode_get_phy_node(fwnode); if (IS_ERR(phy_fwnode)) { - if (phylink_mode_phy(pl->cfg_link_an_mode)) + if (pl->cfg_link_an_mode == MLO_AN_PHY) return -ENODEV; return 0; } @@ -2227,6 +2420,9 @@ int phylink_fwnode_phy_connect(struct phylink *pl, pl->link_config.interface = pl->link_interface; } + if (pl->config->mac_requires_rxc) + flags |= PHY_F_RXC_ALWAYS_ON; + ret = phy_attach_direct(pl->netdev, phy_dev, flags, pl->link_interface); phy_device_free(phy_dev); @@ -2259,6 +2455,8 @@ void phylink_disconnect_phy(struct phylink *pl) mutex_lock(&phy->lock); mutex_lock(&pl->state_mutex); pl->phydev = NULL; + pl->phy_enable_tx_lpi = false; + pl->mac_tx_clk_stop = false; mutex_unlock(&pl->state_mutex); mutex_unlock(&phy->lock); flush_work(&pl->resolve); @@ -2271,7 +2469,7 @@ EXPORT_SYMBOL_GPL(phylink_disconnect_phy); static void phylink_link_changed(struct phylink *pl, bool up, const char *what) { if (!up) - pl->mac_link_dropped = true; + pl->link_failed = true; phylink_run_resolve(pl); phylink_dbg(pl, "%s link %s\n", what, up ? "up" : "down"); } @@ -2334,7 +2532,7 @@ void phylink_start(struct phylink *pl) ASSERT_RTNL(); phylink_info(pl, "configuring for %s/%s link mode\n", - phylink_an_mode_str(pl->cur_link_an_mode), + phylink_an_mode_str(pl->req_link_an_mode), phy_modes(pl->link_config.interface)); /* Always set the carrier off */ @@ -2357,7 +2555,7 @@ void phylink_start(struct phylink *pl) phylink_enable_and_run_resolve(pl, PHYLINK_DISABLE_STOPPED); - if (phylink_mode_fixed(pl->cfg_link_an_mode) && pl->link_gpio) { + if (pl->cfg_link_an_mode == MLO_AN_FIXED && pl->link_gpio) { int irq = gpiod_to_irq(pl->link_gpio); if (irq > 0) { @@ -2437,7 +2635,7 @@ void phylink_suspend(struct phylink *pl, bool mac_wol) { ASSERT_RTNL(); - if (mac_wol && (!pl->netdev || pl->netdev->wol_enabled)) { + if (mac_wol && (!pl->netdev || pl->netdev->ethtool->wol_enabled)) { /* Wake-on-Lan enabled, MAC handling */ mutex_lock(&pl->state_mutex); @@ -2464,6 +2662,30 @@ void phylink_suspend(struct phylink *pl, bool mac_wol) EXPORT_SYMBOL_GPL(phylink_suspend); /** + * phylink_prepare_resume() - prepare to resume a network device + * @pl: a pointer to a &struct phylink returned from phylink_create() + * + * Optional, thus must be called prior to phylink_resume(). + * + * Prepare to resume a network device, preparing the PHY as necessary. + */ +void phylink_prepare_resume(struct phylink *pl) +{ + ASSERT_RTNL(); + + /* If the MAC requires the receive clock, but receive clock + * stop was enabled at the PHY, we need to ensure that the + * receive clock is running. Disable receive clock stop. + * phylink_resume() will re-enable it if necessary. + */ + if (pl->mac_supports_eee_ops && pl->phydev && + pl->config->mac_requires_rxc && + pl->config->eee_rx_clk_stop_enable) + phy_eee_rx_clock_stop(pl->phydev, false); +} +EXPORT_SYMBOL_GPL(phylink_prepare_resume); + +/** * phylink_resume() - handle a network device resume event * @pl: a pointer to a &struct phylink returned from phylink_create() * @@ -2472,8 +2694,22 @@ EXPORT_SYMBOL_GPL(phylink_suspend); */ void phylink_resume(struct phylink *pl) { + int ret; + ASSERT_RTNL(); + if (pl->mac_supports_eee_ops && pl->phydev) { + /* Explicitly configure whether the PHY is allowed to stop its + * receive clock on resume to ensure that it is correctly + * configured. + */ + ret = phy_eee_rx_clock_stop(pl->phydev, + pl->config->eee_rx_clk_stop_enable); + if (ret == -EOPNOTSUPP) + phylink_warn(pl, "failed to set rx clock stop: %pe\n", + ERR_PTR(ret)); + } + if (test_bit(PHYLINK_DISABLE_MAC_WOL, &pl->phylink_disable_state)) { /* Wake-on-Lan enabled, MAC handling */ @@ -2545,6 +2781,32 @@ int phylink_ethtool_set_wol(struct phylink *pl, struct ethtool_wolinfo *wol) } EXPORT_SYMBOL_GPL(phylink_ethtool_set_wol); +static phy_interface_t phylink_sfp_select_interface(struct phylink *pl, + const unsigned long *link_modes) +{ + phy_interface_t interface; + + interface = sfp_select_interface(pl->sfp_bus, link_modes); + if (interface == PHY_INTERFACE_MODE_NA) { + phylink_err(pl, + "selection of interface failed, advertisement %*pb\n", + __ETHTOOL_LINK_MODE_MASK_NBITS, + link_modes); + return interface; + } + + if (!test_bit(interface, pl->config->supported_interfaces)) { + phylink_err(pl, + "selection of interface failed, SFP selected %s (%u) but MAC supports %*pbl\n", + phy_modes(interface), interface, + (int)PHY_INTERFACE_MODE_MAX, + pl->config->supported_interfaces); + return PHY_INTERFACE_MODE_NA; + } + + return interface; +} + static void phylink_merge_link_mode(unsigned long *dst, const unsigned long *b) { __ETHTOOL_DECLARE_LINK_MODE_MASK(mask); @@ -2593,7 +2855,7 @@ int phylink_ethtool_ksettings_get(struct phylink *pl, linkmode_copy(kset->link_modes.supported, pl->supported); - switch (pl->cur_link_an_mode) { + switch (pl->act_link_an_mode) { case MLO_AN_FIXED: /* We are using fixed settings. Report these as the * current link settings - and note that these also @@ -2624,37 +2886,24 @@ int phylink_ethtool_ksettings_get(struct phylink *pl, } EXPORT_SYMBOL_GPL(phylink_ethtool_ksettings_get); -static int phylink_query_inband(struct phylink *pl, phy_interface_t interface) +static bool phylink_validate_pcs_inband_autoneg(struct phylink *pl, + phy_interface_t interface, + unsigned long *adv) { - struct phylink_pcs *pcs; - - if (!pl->using_mac_select_pcs) - return 0; - - pcs = pl->mac_ops->mac_select_pcs(pl->config, interface); - if (!pcs) - return 0; - - return phylink_pcs_query_inband(pcs, interface); -} - -static bool phylink_validate_pcs_an(struct phylink *pl, - phy_interface_t interface, bool an_state) -{ - int link_inband = phylink_query_inband(pl, interface); + unsigned int inband = phylink_inband_caps(pl, interface); + unsigned int mask; /* If the PCS doesn't implement inband support, be permissive. */ - if (!(link_inband & LINK_INBAND_VALID)) + if (!inband) return true; - /* If the PCS can configure the use of inband, allow either state */ - if (link_inband & LINK_INBAND_POSSIBLE) - return true; + if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, adv)) + mask = LINK_INBAND_ENABLE; + else + mask = LINK_INBAND_DISABLE; - /* Otherwise, require the AN state to be fixed according to the - * required flag. - */ - return an_state == !!(link_inband & LINK_INBAND_REQUIRED); + /* Check whether the PCS implements the required mode */ + return !!(inband & mask); } /** @@ -2718,7 +2967,7 @@ int phylink_ethtool_ksettings_set(struct phylink *pl, /* If we have a fixed link, refuse to change link parameters. * If the link parameters match, accept them but do nothing. */ - if (phylink_mode_fixed(pl->cur_link_an_mode)) { + if (pl->req_link_an_mode == MLO_AN_FIXED) { if (s->speed != pl->link_config.speed || s->duplex != pl->link_config.duplex) return -EINVAL; @@ -2734,7 +2983,7 @@ int phylink_ethtool_ksettings_set(struct phylink *pl, * is our default case) but do not allow the advertisement to * be changed. If the advertisement matches, simply return. */ - if (phylink_mode_fixed(pl->cur_link_an_mode)) { + if (pl->req_link_an_mode == MLO_AN_FIXED) { if (!linkmode_equal(config.advertising, pl->link_config.advertising)) return -EINVAL; @@ -2760,22 +3009,16 @@ int phylink_ethtool_ksettings_set(struct phylink *pl, * link can be configured correctly. */ if (pl->sfp_bus) { - config.interface = sfp_select_interface(pl->sfp_bus, + config.interface = phylink_sfp_select_interface(pl, config.advertising); - if (config.interface == PHY_INTERFACE_MODE_NA) { - phylink_err(pl, - "selection of interface failed, advertisement %*pb\n", - __ETHTOOL_LINK_MODE_MASK_NBITS, - config.advertising); + if (config.interface == PHY_INTERFACE_MODE_NA) return -EINVAL; - } /* Revalidate with the selected interface */ linkmode_copy(support, pl->supported); - if (phylink_validate(pl, pl->cur_link_an_mode, support, - &config)) { + if (phylink_validate(pl, support, &config)) { phylink_err(pl, "validation of %s/%s with support %*pb failed\n", - phylink_an_mode_str(pl->cur_link_an_mode), + phylink_an_mode_str(pl->req_link_an_mode), phy_modes(config.interface), __ETHTOOL_LINK_MODE_MASK_NBITS, support); return -EINVAL; @@ -2783,8 +3026,7 @@ int phylink_ethtool_ksettings_set(struct phylink *pl, } else { /* Validate without changing the current supported mask. */ linkmode_copy(support, pl->supported); - if (phylink_validate(pl, pl->cur_link_an_mode, support, - &config)) + if (phylink_validate(pl, support, &config)) return -EINVAL; } @@ -2797,9 +3039,8 @@ int phylink_ethtool_ksettings_set(struct phylink *pl, /* Validate the autonegotiation state. We don't have a PHY in this * situation, so the PCS is the media-facing entity. */ - if (!phylink_validate_pcs_an(pl, config.interface, - linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, - config.advertising))) + if (!phylink_validate_pcs_inband_autoneg(pl, config.interface, + config.advertising)) return -EINVAL; mutex_lock(&pl->state_mutex); @@ -2884,7 +3125,7 @@ int phylink_ethtool_set_pauseparam(struct phylink *pl, ASSERT_RTNL(); - if (phylink_mode_fixed(pl->cur_link_an_mode)) + if (pl->req_link_an_mode == MLO_AN_FIXED) return -EOPNOTSUPP; if (!phylink_test(pl->supported, Pause) && @@ -2948,7 +3189,7 @@ int phylink_ethtool_set_pauseparam(struct phylink *pl, * link will cycle. */ if (manual_changed) { - pl->mac_link_dropped = true; + pl->link_failed = true; phylink_run_resolve(pl); } @@ -2992,8 +3233,6 @@ int phylink_init_eee(struct phylink *pl, bool clk_stop_enable) if (pl->phydev) ret = phy_init_eee(pl->phydev, clk_stop_enable); - else if (pl->sfp_bus && phylink_mac_supports_eee(pl)) - ret = 0; return ret; } @@ -3002,26 +3241,23 @@ EXPORT_SYMBOL_GPL(phylink_init_eee); /** * phylink_ethtool_get_eee() - read the energy efficient ethernet parameters * @pl: a pointer to a &struct phylink returned from phylink_create() - * @eee: a pointer to a &struct ethtool_eee for the read parameters + * @eee: a pointer to a &struct ethtool_keee for the read parameters */ -int phylink_ethtool_get_eee(struct phylink *pl, struct ethtool_eee *eee) +int phylink_ethtool_get_eee(struct phylink *pl, struct ethtool_keee *eee) { int ret = -EOPNOTSUPP; ASSERT_RTNL(); + if (pl->mac_supports_eee_ops && !pl->mac_supports_eee) + return ret; + if (pl->phydev) { ret = phy_ethtool_get_eee(pl->phydev, eee); - } else if (pl->sfp_bus && phylink_mac_supports_eee(pl)) { - /* For optical SFPs, ensure that eee->supported is nonzero. */ - eee->supported = SUPPORTED_FIBRE; - ret = 0; - } - - if (!ret && phylink_mac_supports_eee(pl)) { - /* Overwrite phylib's interpretation of configuration */ - eeecfg_to_eee(&pl->eee_cfg, eee); - eee->eee_active = pl->eee_active; + /* Restrict supported linkmode mask */ + if (ret == 0 && pl->mac_supports_eee_ops) + linkmode_and(eee->supported, eee->supported, + pl->supported_lpi); } return ret; @@ -3031,56 +3267,32 @@ EXPORT_SYMBOL_GPL(phylink_ethtool_get_eee); /** * phylink_ethtool_set_eee() - set the energy efficient ethernet parameters * @pl: a pointer to a &struct phylink returned from phylink_create() - * @eee: a pointer to a &struct ethtool_eee for the desired parameters + * @eee: a pointer to a &struct ethtool_keee for the desired parameters */ -int phylink_ethtool_set_eee(struct phylink *pl, struct ethtool_eee *eee) +int phylink_ethtool_set_eee(struct phylink *pl, struct ethtool_keee *eee) { + bool mac_eee = pl->mac_supports_eee; int ret = -EOPNOTSUPP; - bool mac_eee; ASSERT_RTNL(); - mac_eee = phylink_mac_supports_eee(pl); - - phylink_dbg(pl, "mac %s phylink EEE%s, adv 0x%08x, LPI%s timer %uus\n", + phylink_dbg(pl, "mac %s phylink EEE%s, adv %*pbl, LPI%s timer %uus\n", mac_eee ? "supports" : "does not support", - eee->eee_enabled ? ", enabled" : "", eee->advertised, + eee->eee_enabled ? ", enabled" : "", + __ETHTOOL_LINK_MODE_MASK_NBITS, eee->advertised, eee->tx_lpi_enabled ? " enabled" : "", eee->tx_lpi_timer); - /* Clamp the LPI timer maximum value */ - if (mac_eee && eee->tx_lpi_timer > pl->config->lpi_timer_limit_us) { - eee->tx_lpi_timer = pl->config->lpi_timer_limit_us; - phylink_dbg(pl, "LPI timer limited to %uus\n", - eee->tx_lpi_timer); - } + if (pl->mac_supports_eee_ops && !mac_eee) + return ret; - if (pl->phydev) + if (pl->phydev) { + /* Restrict advertisement mask */ + if (pl->mac_supports_eee_ops) + linkmode_and(eee->advertised, eee->advertised, + pl->supported_lpi); ret = phy_ethtool_set_eee(pl->phydev, eee); - else if (pl->sfp_bus && phylink_mac_supports_eee(pl)) - ret = 0; - - if (!ret && mac_eee) { - bool can_lpi, old_can_lpi; - - mutex_lock(&pl->state_mutex); - old_can_lpi = eeecfg_mac_can_tx_lpi(&pl->eee_cfg); - eee_to_eeecfg(eee, &pl->eee_cfg); - can_lpi = eeecfg_mac_can_tx_lpi(&pl->eee_cfg); - - phylink_dbg(pl, "can_lpi %u -> %u\n", old_can_lpi, can_lpi); - - /* If the link is up, and the configuration changes the - * LPI permissive state, deal with the change at the MAC. - */ - if (phylink_link_is_up(pl) && old_can_lpi != can_lpi) { - phylink_dbg(pl, "link is up, lpi changed\n"); - if (can_lpi) - phylink_activate_eee(pl); - else - phylink_deactivate_eee(pl); - } - - mutex_unlock(&pl->state_mutex); + if (ret == 0) + eee_to_eeecfg(&pl->eee_cfg, eee); } return ret; @@ -3202,7 +3414,7 @@ static int phylink_mii_read(struct phylink *pl, unsigned int phy_id, struct phylink_link_state state; int val = 0xffff; - switch (pl->cur_link_an_mode) { + switch (pl->act_link_an_mode) { case MLO_AN_FIXED: if (phy_id == 0) { phylink_get_fixed_state(pl, &state); @@ -3227,7 +3439,7 @@ static int phylink_mii_read(struct phylink *pl, unsigned int phy_id, static int phylink_mii_write(struct phylink *pl, unsigned int phy_id, unsigned int reg, unsigned int val) { - switch (pl->cur_link_an_mode) { + switch (pl->act_link_an_mode) { case MLO_AN_FIXED: break; @@ -3397,11 +3609,11 @@ static phy_interface_t phylink_choose_sfp_interface(struct phylink *pl, return interface; } -static void phylink_sfp_set_config(struct phylink *pl, u8 mode, - unsigned long *supported, - struct phylink_link_state *state) +static void phylink_sfp_set_config(struct phylink *pl, unsigned long *supported, + struct phylink_link_state *state, + bool changed) { - bool changed = false; + u8 mode = MLO_AN_INBAND; phylink_dbg(pl, "requesting link mode %s/%s with support %*pb\n", phylink_an_mode_str(mode), phy_modes(state->interface), @@ -3417,9 +3629,9 @@ static void phylink_sfp_set_config(struct phylink *pl, u8 mode, changed = true; } - if (pl->cur_link_an_mode != mode || + if (pl->req_link_an_mode != mode || pl->link_config.interface != state->interface) { - pl->cur_link_an_mode = mode; + pl->req_link_an_mode = mode; pl->link_config.interface = state->interface; changed = true; @@ -3436,10 +3648,8 @@ static void phylink_sfp_set_config(struct phylink *pl, u8 mode, static int phylink_sfp_config_phy(struct phylink *pl, struct phy_device *phy) { - __ETHTOOL_DECLARE_LINK_MODE_MASK(support1); __ETHTOOL_DECLARE_LINK_MODE_MASK(support); struct phylink_link_state config; - phy_interface_t iface; int ret; linkmode_copy(support, phy->supported); @@ -3452,7 +3662,7 @@ static int phylink_sfp_config_phy(struct phylink *pl, struct phy_device *phy) config.pause = MLO_PAUSE_AN; /* Ignore errors if we're expecting a PHY to attach later */ - ret = phylink_validate(pl, pl->sfp_link_an_mode, support, &config); + ret = phylink_validate(pl, support, &config); if (ret) { phylink_err(pl, "validation with support %*pb failed: %pe\n", __ETHTOOL_LINK_MODE_MASK_NBITS, support, @@ -3460,30 +3670,27 @@ static int phylink_sfp_config_phy(struct phylink *pl, struct phy_device *phy) return ret; } - iface = sfp_select_interface(pl->sfp_bus, config.advertising); - if (iface == PHY_INTERFACE_MODE_NA) { - phylink_err(pl, - "selection of interface failed, advertisement %*pb\n", - __ETHTOOL_LINK_MODE_MASK_NBITS, config.advertising); + config.interface = phylink_sfp_select_interface(pl, config.advertising); + if (config.interface == PHY_INTERFACE_MODE_NA) return -EINVAL; - } - config.interface = iface; - linkmode_copy(support1, support); - ret = phylink_validate(pl, pl->sfp_link_an_mode, support1, &config); - if (ret) { - phylink_err(pl, - "validation of %s/%s with support %*pb failed: %pe\n", - phylink_an_mode_str(pl->sfp_link_an_mode), - phy_modes(config.interface), - __ETHTOOL_LINK_MODE_MASK_NBITS, support, - ERR_PTR(ret)); + /* Attach the PHY so that the PHY is present when we do the major + * configuration step. + */ + ret = phylink_attach_phy(pl, phy, config.interface); + if (ret < 0) + return ret; + + /* This will validate the configuration for us. */ + ret = phylink_bringup_phy(pl, phy, config.interface); + if (ret < 0) { + phy_detach(phy); return ret; } pl->link_port = pl->sfp_port; - phylink_sfp_set_config(pl, pl->sfp_link_an_mode, support, &config); + phylink_sfp_set_config(pl, support, &config, true); return 0; } @@ -3522,8 +3729,8 @@ static int phylink_sfp_config_optical(struct phylink *pl) /* For all the interfaces that are supported, reduce the sfp_support * mask to only those link modes that can be supported. */ - ret = phylink_validate_mask(pl, NULL, MLO_AN_INBAND, pl->sfp_support, - &config, interfaces); + ret = phylink_validate_mask(pl, NULL, pl->sfp_support, &config, + interfaces); if (ret) { phylink_err(pl, "unsupported SFP module: validation with support %*pb failed\n", __ETHTOOL_LINK_MODE_MASK_NBITS, support); @@ -3539,9 +3746,8 @@ static int phylink_sfp_config_optical(struct phylink *pl) phylink_dbg(pl, "optical SFP: chosen %s interface\n", phy_modes(interface)); - if (!phylink_validate_pcs_an(pl, interface, - linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, - config.advertising))) { + if (!phylink_validate_pcs_inband_autoneg(pl, interface, + config.advertising)) { phylink_err(pl, "autoneg setting not compatible with PCS"); return -EINVAL; } @@ -3549,7 +3755,7 @@ static int phylink_sfp_config_optical(struct phylink *pl) config.interface = interface; /* Ignore errors if we're expecting a PHY to attach later */ - ret = phylink_validate(pl, MLO_AN_INBAND, support, &config); + ret = phylink_validate(pl, support, &config); if (ret) { phylink_err(pl, "validation with support %*pb failed: %pe\n", __ETHTOOL_LINK_MODE_MASK_NBITS, support, @@ -3559,7 +3765,7 @@ static int phylink_sfp_config_optical(struct phylink *pl) pl->link_port = pl->sfp_port; - phylink_sfp_set_config(pl, MLO_AN_INBAND, pl->sfp_support, &config); + phylink_sfp_set_config(pl, pl->sfp_support, &config, false); return 0; } @@ -3575,7 +3781,6 @@ static int phylink_sfp_module_insert(void *upstream, phy_interface_zero(pl->sfp_interfaces); sfp_parse_support(pl->sfp_bus, id, pl->sfp_support, pl->sfp_interfaces); pl->sfp_port = sfp_parse_port(pl->sfp_bus, id, pl->sfp_support); - pl->sfp_link_an_mode = MLO_AN_INBAND; /* If this module may have a PHY connecting later, defer until later */ pl->sfp_may_have_phy = sfp_may_have_phy(pl->sfp_bus, id); @@ -3636,7 +3841,14 @@ static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy) DECLARE_PHY_INTERFACE_MASK(interfaces); struct phylink *pl = upstream; phy_interface_t interface; - int link_inband, ret; + int ret; + + if (!phy->drv) { + phylink_err(pl, "PHY %s (id 0x%.8lx) has no driver loaded\n", + phydev_name(phy), (unsigned long)phy->phy_id); + phylink_err(pl, "Drivers which handle known common cases: CONFIG_BCM84881_PHY, CONFIG_MARVELL_PHY\n"); + return -EINVAL; + } /* * This is the new way of dealing with flow control for PHYs, @@ -3655,67 +3867,55 @@ static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy) phylink_dbg(pl, "copper SFP: PHY provides empty supported_interfaces\n"); /* Do the initial configuration */ - ret = phylink_sfp_config_phy(pl, phy); - if (ret < 0) - return ret; - } else { - phylink_dbg(pl, "copper SFP: interfaces=[mac=%*pbl, sfp=%*pbl]\n", - (int)PHY_INTERFACE_MODE_MAX, - pl->config->supported_interfaces, - (int)PHY_INTERFACE_MODE_MAX, - phy->supported_interfaces); - - phy_interface_and(interfaces, phy->supported_interfaces, - pl->config->supported_interfaces); - interface = phylink_choose_sfp_interface(pl, interfaces); - if (interface == PHY_INTERFACE_MODE_NA) { - phylink_err(pl, - "selection of interface for PHY failed\n"); - return -EINVAL; - } - - phylink_dbg(pl, "copper SFP: chosen %s interface\n", - phy_modes(interface)); - - link_inband = phy_query_inband(phy, interface); - phylink_dbg(pl, "copper SFP: PHY link in-band modes 0x%x\n", - link_inband); - - /* If the link inband is valid but the PHY doesn't support - * inband, then we have to use PHY mode. E.g. BCM84881. - */ - if (link_inband == LINK_INBAND_VALID) - pl->sfp_link_an_mode = MLO_AN_PHY; - - if (pl->cur_link_an_mode != pl->sfp_link_an_mode || - pl->link_config.interface != interface) { - pl->link_config.interface = interface; - pl->cur_link_an_mode = pl->sfp_link_an_mode; + return phylink_sfp_config_phy(pl, phy); + } - phylink_info(pl, "switched to %s/%s link mode\n", - phylink_an_mode_str(pl->sfp_link_an_mode), - phy_modes(interface)); - } + phylink_dbg(pl, "copper SFP: interfaces=[mac=%*pbl, sfp=%*pbl]\n", + (int)PHY_INTERFACE_MODE_MAX, + pl->config->supported_interfaces, + (int)PHY_INTERFACE_MODE_MAX, + phy->supported_interfaces); - if (!test_bit(PHYLINK_DISABLE_STOPPED, - &pl->phylink_disable_state)) - phylink_mac_initial_config(pl, false); + phy_interface_and(interfaces, phy->supported_interfaces, + pl->config->supported_interfaces); + interface = phylink_choose_sfp_interface(pl, interfaces); + if (interface == PHY_INTERFACE_MODE_NA) { + phylink_err(pl, + "selection of interface for PHY failed\n"); + return -EINVAL; } - interface = pl->link_config.interface; + phylink_dbg(pl, "copper SFP: chosen %s interface\n", + phy_modes(interface)); ret = phylink_attach_phy(pl, phy, interface); if (ret < 0) return ret; ret = phylink_bringup_phy(pl, phy, interface); - if (ret) + if (ret) { phy_detach(phy); + return ret; + } - return ret; + if (pl->req_link_an_mode != MLO_AN_INBAND || + pl->link_config.interface != interface) { + pl->link_config.interface = interface; + pl->req_link_an_mode = MLO_AN_INBAND; + + phylink_info(pl, "switched to %s/%s link mode\n", + phylink_an_mode_str(pl->req_link_an_mode), + phy_modes(pl->link_config.interface)); + } + + if (!test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) + phylink_mac_initial_config(pl, false); + + return 0; } -static void phylink_sfp_disconnect_phy(void *upstream) +static void phylink_sfp_disconnect_phy(void *upstream, + struct phy_device *phydev) { phylink_disconnect_phy(upstream); } @@ -3912,6 +4112,7 @@ static void phylink_decode_usgmii_word(struct phylink_link_state *state, /** * phylink_mii_c22_pcs_decode_state() - Decode MAC PCS state from MII registers * @state: a pointer to a &struct phylink_link_state. + * @neg_mode: link negotiation mode (PHYLINK_PCS_NEG_xxx) * @bmsr: The value of the %MII_BMSR register * @lpa: The value of the %MII_LPA register * @@ -3924,32 +4125,45 @@ static void phylink_decode_usgmii_word(struct phylink_link_state *state, * accessing @bmsr and @lpa cannot be done with MDIO directly. */ void phylink_mii_c22_pcs_decode_state(struct phylink_link_state *state, - u16 bmsr, u16 lpa) + unsigned int neg_mode, u16 bmsr, u16 lpa) { state->link = !!(bmsr & BMSR_LSTATUS); state->an_complete = !!(bmsr & BMSR_ANEGCOMPLETE); - /* If there is no link or autonegotiation is disabled, the LP advertisement - * data is not meaningful, so don't go any further. - */ - if (!state->link || !linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, - state->advertising)) + + /* If the link is down, the advertisement data is undefined. */ + if (!state->link) return; switch (state->interface) { case PHY_INTERFACE_MODE_1000BASEX: - phylink_decode_c37_word(state, lpa, SPEED_1000); + if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) { + phylink_decode_c37_word(state, lpa, SPEED_1000); + } else { + state->speed = SPEED_1000; + state->duplex = DUPLEX_FULL; + state->pause |= MLO_PAUSE_TX | MLO_PAUSE_RX; + } break; case PHY_INTERFACE_MODE_2500BASEX: - phylink_decode_c37_word(state, lpa, SPEED_2500); + if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) { + phylink_decode_c37_word(state, lpa, SPEED_2500); + } else { + state->speed = SPEED_2500; + state->duplex = DUPLEX_FULL; + state->pause |= MLO_PAUSE_TX | MLO_PAUSE_RX; + } break; case PHY_INTERFACE_MODE_SGMII: case PHY_INTERFACE_MODE_QSGMII: - phylink_decode_sgmii_word(state, lpa); + if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) + phylink_decode_sgmii_word(state, lpa); break; + case PHY_INTERFACE_MODE_QUSGMII: - phylink_decode_usgmii_word(state, lpa); + if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) + phylink_decode_usgmii_word(state, lpa); break; default: @@ -3962,6 +4176,7 @@ EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_decode_state); /** * phylink_mii_c22_pcs_get_state() - read the MAC PCS state * @pcs: a pointer to a &struct mdio_device. + * @neg_mode: link negotiation mode (PHYLINK_PCS_NEG_xxx) * @state: a pointer to a &struct phylink_link_state. * * Helper for MAC PCS supporting the 802.3 clause 22 register set for @@ -3974,6 +4189,7 @@ EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_decode_state); * structure. */ void phylink_mii_c22_pcs_get_state(struct mdio_device *pcs, + unsigned int neg_mode, struct phylink_link_state *state) { int bmsr, lpa; @@ -3985,7 +4201,7 @@ void phylink_mii_c22_pcs_get_state(struct mdio_device *pcs, return; } - phylink_mii_c22_pcs_decode_state(state, bmsr, lpa); + phylink_mii_c22_pcs_decode_state(state, neg_mode, bmsr, lpa); } EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_get_state); |