diff options
Diffstat (limited to 'drivers/net/phy/phylink.c')
-rw-r--r-- | drivers/net/phy/phylink.c | 561 |
1 files changed, 247 insertions, 314 deletions
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index b00a315de060..69ca765485db 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -20,6 +20,7 @@ #include <linux/timer.h> #include <linux/workqueue.h> +#include "phy-caps.h" #include "sfp.h" #include "swphy.h" @@ -71,8 +72,6 @@ struct phylink { struct gpio_desc *link_gpio; unsigned int link_irq; struct timer_list link_poll; - void (*get_fixed_state)(struct net_device *dev, - struct phylink_link_state *s); struct mutex state_mutex; struct phylink_link_state phy_state; @@ -82,12 +81,14 @@ struct phylink { unsigned int pcs_state; bool link_failed; + bool major_config_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; + u8 mac_rx_clk_stop_blocked; struct sfp_bus *sfp_bus; bool sfp_may_have_phy; @@ -291,6 +292,61 @@ static int phylink_interface_max_speed(phy_interface_t interface) return SPEED_UNKNOWN; } +static struct { + unsigned long mask; + int speed; + unsigned int duplex; + unsigned int caps_bit; +} phylink_caps_params[] = { + { MAC_400000FD, SPEED_400000, DUPLEX_FULL, BIT(LINK_CAPA_400000FD) }, + { MAC_200000FD, SPEED_200000, DUPLEX_FULL, BIT(LINK_CAPA_200000FD) }, + { MAC_100000FD, SPEED_100000, DUPLEX_FULL, BIT(LINK_CAPA_100000FD) }, + { MAC_56000FD, SPEED_56000, DUPLEX_FULL, BIT(LINK_CAPA_56000FD) }, + { MAC_50000FD, SPEED_50000, DUPLEX_FULL, BIT(LINK_CAPA_50000FD) }, + { MAC_40000FD, SPEED_40000, DUPLEX_FULL, BIT(LINK_CAPA_40000FD) }, + { MAC_25000FD, SPEED_25000, DUPLEX_FULL, BIT(LINK_CAPA_25000FD) }, + { MAC_20000FD, SPEED_20000, DUPLEX_FULL, BIT(LINK_CAPA_20000FD) }, + { MAC_10000FD, SPEED_10000, DUPLEX_FULL, BIT(LINK_CAPA_10000FD) }, + { MAC_5000FD, SPEED_5000, DUPLEX_FULL, BIT(LINK_CAPA_5000FD) }, + { MAC_2500FD, SPEED_2500, DUPLEX_FULL, BIT(LINK_CAPA_2500FD) }, + { MAC_1000FD, SPEED_1000, DUPLEX_FULL, BIT(LINK_CAPA_1000FD) }, + { MAC_1000HD, SPEED_1000, DUPLEX_HALF, BIT(LINK_CAPA_1000HD) }, + { MAC_100FD, SPEED_100, DUPLEX_FULL, BIT(LINK_CAPA_100FD) }, + { MAC_100HD, SPEED_100, DUPLEX_HALF, BIT(LINK_CAPA_100HD) }, + { MAC_10FD, SPEED_10, DUPLEX_FULL, BIT(LINK_CAPA_10FD) }, + { MAC_10HD, SPEED_10, DUPLEX_HALF, BIT(LINK_CAPA_10HD) }, +}; + +/** + * phylink_caps_to_link_caps() - Convert a set of MAC capabilities LINK caps + * @caps: A set of MAC capabilities + * + * Returns: The corresponding set of LINK_CAPA as defined in phy-caps.h + */ +static unsigned long phylink_caps_to_link_caps(unsigned long caps) +{ + unsigned long link_caps = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(phylink_caps_params); i++) + if (caps & phylink_caps_params[i].mask) + link_caps |= phylink_caps_params[i].caps_bit; + + return link_caps; +} + +static unsigned long phylink_link_caps_to_mac_caps(unsigned long link_caps) +{ + unsigned long caps = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(phylink_caps_params); i++) + if (link_caps & phylink_caps_params[i].caps_bit) + caps |= phylink_caps_params[i].mask; + + return caps; +} + /** * phylink_caps_to_linkmodes() - Convert capabilities to ethtool link modes * @linkmodes: ethtool linkmode mask (must be already initialised) @@ -302,172 +358,17 @@ static int phylink_interface_max_speed(phy_interface_t interface) static void phylink_caps_to_linkmodes(unsigned long *linkmodes, unsigned long caps) { + unsigned long link_caps = phylink_caps_to_link_caps(caps); + if (caps & MAC_SYM_PAUSE) __set_bit(ETHTOOL_LINK_MODE_Pause_BIT, linkmodes); if (caps & MAC_ASYM_PAUSE) __set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, linkmodes); - if (caps & MAC_10HD) { - __set_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_10baseT1S_Half_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_10baseT1S_P2MP_Half_BIT, linkmodes); - } - - if (caps & MAC_10FD) { - __set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_10baseT1S_Full_BIT, linkmodes); - } - - if (caps & MAC_100HD) { - __set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_100baseFX_Half_BIT, linkmodes); - } - - if (caps & MAC_100FD) { - __set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_100baseT1_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_100baseFX_Full_BIT, linkmodes); - } - - if (caps & MAC_1000HD) - __set_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, linkmodes); - - if (caps & MAC_1000FD) { - __set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_1000baseT1_Full_BIT, linkmodes); - } - - if (caps & MAC_2500FD) { - __set_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT, linkmodes); - } - - if (caps & MAC_5000FD) - __set_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, linkmodes); - - if (caps & MAC_10000FD) { - __set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_10000baseR_FEC_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_10000baseCR_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_10000baseSR_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_10000baseLR_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_10000baseER_Full_BIT, linkmodes); - } - - if (caps & MAC_25000FD) { - __set_bit(ETHTOOL_LINK_MODE_25000baseCR_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_25000baseKR_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_25000baseSR_Full_BIT, linkmodes); - } - - if (caps & MAC_40000FD) { - __set_bit(ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT, linkmodes); - } - - if (caps & MAC_50000FD) { - __set_bit(ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_50000baseKR_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_50000baseSR_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_50000baseCR_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT, - linkmodes); - __set_bit(ETHTOOL_LINK_MODE_50000baseDR_Full_BIT, linkmodes); - } - - if (caps & MAC_56000FD) { - __set_bit(ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT, linkmodes); - } - - if (caps & MAC_100000FD) { - __set_bit(ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT, - linkmodes); - __set_bit(ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT, - linkmodes); - __set_bit(ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_100000baseKR_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_100000baseSR_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_100000baseLR_ER_FR_Full_BIT, - linkmodes); - __set_bit(ETHTOOL_LINK_MODE_100000baseCR_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_100000baseDR_Full_BIT, linkmodes); - } - - if (caps & MAC_200000FD) { - __set_bit(ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT, - linkmodes); - __set_bit(ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT, - linkmodes); - __set_bit(ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT, linkmodes); - } - - if (caps & MAC_400000FD) { - __set_bit(ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT, - linkmodes); - __set_bit(ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_400000baseKR4_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT, - linkmodes); - __set_bit(ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT, linkmodes); - __set_bit(ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT, linkmodes); - } + phy_caps_linkmodes(link_caps, linkmodes); } -static struct { - unsigned long mask; - int speed; - unsigned int duplex; -} phylink_caps_params[] = { - { MAC_400000FD, SPEED_400000, DUPLEX_FULL }, - { MAC_200000FD, SPEED_200000, DUPLEX_FULL }, - { MAC_100000FD, SPEED_100000, DUPLEX_FULL }, - { MAC_56000FD, SPEED_56000, DUPLEX_FULL }, - { MAC_50000FD, SPEED_50000, DUPLEX_FULL }, - { MAC_40000FD, SPEED_40000, DUPLEX_FULL }, - { MAC_25000FD, SPEED_25000, DUPLEX_FULL }, - { MAC_20000FD, SPEED_20000, DUPLEX_FULL }, - { MAC_10000FD, SPEED_10000, DUPLEX_FULL }, - { MAC_5000FD, SPEED_5000, DUPLEX_FULL }, - { MAC_2500FD, SPEED_2500, DUPLEX_FULL }, - { MAC_1000FD, SPEED_1000, DUPLEX_FULL }, - { MAC_1000HD, SPEED_1000, DUPLEX_HALF }, - { MAC_100FD, SPEED_100, DUPLEX_FULL }, - { MAC_100HD, SPEED_100, DUPLEX_HALF }, - { MAC_10FD, SPEED_10, DUPLEX_FULL }, - { MAC_10HD, SPEED_10, DUPLEX_HALF }, -}; - /** * phylink_limit_mac_speed - limit the phylink_config to a maximum speed * @config: pointer to a &struct phylink_config @@ -523,86 +424,12 @@ static unsigned long phylink_get_capabilities(phy_interface_t interface, unsigned long mac_capabilities, int rate_matching) { + unsigned long link_caps = phy_caps_from_interface(interface); int max_speed = phylink_interface_max_speed(interface); unsigned long caps = MAC_SYM_PAUSE | MAC_ASYM_PAUSE; unsigned long matched_caps = 0; - switch (interface) { - case PHY_INTERFACE_MODE_USXGMII: - caps |= MAC_10000FD | MAC_5000FD; - fallthrough; - - case PHY_INTERFACE_MODE_10G_QXGMII: - caps |= MAC_2500FD; - fallthrough; - - case PHY_INTERFACE_MODE_RGMII_TXID: - case PHY_INTERFACE_MODE_RGMII_RXID: - case PHY_INTERFACE_MODE_RGMII_ID: - case PHY_INTERFACE_MODE_RGMII: - case PHY_INTERFACE_MODE_PSGMII: - case PHY_INTERFACE_MODE_QSGMII: - case PHY_INTERFACE_MODE_QUSGMII: - case PHY_INTERFACE_MODE_SGMII: - case PHY_INTERFACE_MODE_GMII: - caps |= MAC_1000HD | MAC_1000FD; - fallthrough; - - case PHY_INTERFACE_MODE_REVRMII: - case PHY_INTERFACE_MODE_RMII: - case PHY_INTERFACE_MODE_SMII: - case PHY_INTERFACE_MODE_REVMII: - case PHY_INTERFACE_MODE_MII: - caps |= MAC_10HD | MAC_10FD; - fallthrough; - - case PHY_INTERFACE_MODE_100BASEX: - caps |= MAC_100HD | MAC_100FD; - break; - - case PHY_INTERFACE_MODE_TBI: - case PHY_INTERFACE_MODE_MOCA: - case PHY_INTERFACE_MODE_RTBI: - case PHY_INTERFACE_MODE_1000BASEX: - caps |= MAC_1000HD; - fallthrough; - case PHY_INTERFACE_MODE_1000BASEKX: - case PHY_INTERFACE_MODE_TRGMII: - caps |= MAC_1000FD; - break; - - case PHY_INTERFACE_MODE_2500BASEX: - caps |= MAC_2500FD; - break; - - case PHY_INTERFACE_MODE_5GBASER: - caps |= MAC_5000FD; - break; - - case PHY_INTERFACE_MODE_XGMII: - case PHY_INTERFACE_MODE_RXAUI: - case PHY_INTERFACE_MODE_XAUI: - case PHY_INTERFACE_MODE_10GBASER: - case PHY_INTERFACE_MODE_10GKR: - caps |= MAC_10000FD; - break; - - case PHY_INTERFACE_MODE_25GBASER: - caps |= MAC_25000FD; - break; - - case PHY_INTERFACE_MODE_XLGMII: - caps |= MAC_40000FD; - break; - - case PHY_INTERFACE_MODE_INTERNAL: - caps |= ~0; - break; - - case PHY_INTERFACE_MODE_NA: - case PHY_INTERFACE_MODE_MAX: - break; - } + caps |= phylink_link_caps_to_mac_caps(link_caps); switch (rate_matching) { case RATE_MATCH_OPEN_LOOP: @@ -801,12 +628,26 @@ static int phylink_validate(struct phylink *pl, unsigned long *supported, return phylink_validate_mac_and_pcs(pl, supported, state); } +static void phylink_fill_fixedlink_supported(unsigned long *supported) +{ + linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, supported); +} + static int phylink_parse_fixedlink(struct phylink *pl, const struct fwnode_handle *fwnode) { + __ETHTOOL_DECLARE_LINK_MODE_MASK(match) = { 0, }; __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; + const struct link_capabilities *c; struct fwnode_handle *fixed_node; - const struct phy_setting *s; struct gpio_desc *desc; u32 speed; int ret; @@ -874,12 +715,16 @@ static int phylink_parse_fixedlink(struct phylink *pl, phylink_warn(pl, "fixed link specifies half duplex for %dMbps link?\n", pl->link_config.speed); - linkmode_fill(pl->supported); + linkmode_zero(pl->supported); + phylink_fill_fixedlink_supported(pl->supported); + linkmode_copy(pl->link_config.advertising, pl->supported); phylink_validate(pl, pl->supported, &pl->link_config); - s = phy_lookup_setting(pl->link_config.speed, pl->link_config.duplex, - pl->supported, true); + c = phy_caps_lookup(pl->link_config.speed, pl->link_config.duplex, + pl->supported, true); + if (c) + linkmode_and(match, pl->supported, c->linkmodes); linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, mask); linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, mask); @@ -888,9 +733,10 @@ static int phylink_parse_fixedlink(struct phylink *pl, phylink_set(pl->supported, MII); - if (s) { - __set_bit(s->bit, pl->supported); - __set_bit(s->bit, pl->link_config.lp_advertising); + if (c) { + linkmode_or(pl->supported, pl->supported, match); + linkmode_or(pl->link_config.lp_advertising, + pl->link_config.lp_advertising, match); } else { phylink_warn(pl, "fixed link %s duplex %dMbps not recognised\n", pl->link_config.duplex == DUPLEX_FULL ? "full" : "half", @@ -1073,6 +919,18 @@ 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. */ @@ -1353,19 +1211,22 @@ static void phylink_major_config(struct phylink *pl, bool restart, struct phylink_pcs *pcs = NULL; bool pcs_changed = false; unsigned int rate_kbd; - unsigned int neg_mode; int err; phylink_dbg(pl, "major config, requested %s/%s\n", phylink_an_mode_str(pl->req_link_an_mode), phy_modes(state->interface)); + pl->major_config_failed = false; + 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, "mac_select_pcs unexpectedly failed: %pe\n", pcs); + + pl->major_config_failed = true; return; } @@ -1387,6 +1248,7 @@ static void phylink_major_config(struct phylink *pl, bool restart, if (err < 0) { phylink_err(pl, "mac_prepare failed: %pe\n", ERR_PTR(err)); + pl->major_config_failed = true; return; } } @@ -1410,23 +1272,27 @@ static void phylink_major_config(struct phylink *pl, bool restart, phylink_mac_config(pl, state); - if (pl->pcs) - phylink_pcs_post_config(pl->pcs, state->interface); + if (pl->pcs) { + err = phylink_pcs_post_config(pl->pcs, state->interface); + if (err < 0) { + phylink_err(pl, "pcs_post_config failed: %pe\n", + ERR_PTR(err)); + + pl->major_config_failed = true; + } + } if (pl->pcs_state == PCS_STATE_STARTING || pcs_changed) phylink_pcs_enable(pl->pcs); - neg_mode = pl->act_link_an_mode; - if (pl->pcs && pl->pcs->neg_mode) - neg_mode = pl->pcs_neg_mode; - - err = phylink_pcs_config(pl->pcs, neg_mode, state, + err = phylink_pcs_config(pl->pcs, pl->pcs_neg_mode, state, !!(pl->link_config.pause & MLO_PAUSE_AN)); - if (err < 0) - phylink_err(pl, "pcs_config failed: %pe\n", - ERR_PTR(err)); - else if (err > 0) + if (err < 0) { + phylink_err(pl, "pcs_config failed: %pe\n", ERR_PTR(err)); + pl->major_config_failed = true; + } else if (err > 0) { restart = true; + } if (restart) phylink_pcs_an_restart(pl); @@ -1434,16 +1300,22 @@ static void phylink_major_config(struct phylink *pl, bool restart, if (pl->mac_ops->mac_finish) { err = pl->mac_ops->mac_finish(pl->config, pl->act_link_an_mode, state->interface); - if (err < 0) + if (err < 0) { phylink_err(pl, "mac_finish failed: %pe\n", ERR_PTR(err)); + + pl->major_config_failed = true; + } } if (pl->phydev && pl->phy_ib_mode) { err = phy_config_inband(pl->phydev, pl->phy_ib_mode); - if (err < 0) + if (err < 0) { phylink_err(pl, "phy_config_inband: %pe\n", ERR_PTR(err)); + + pl->major_config_failed = true; + } } if (pl->sfp_bus) { @@ -1463,7 +1335,6 @@ static void phylink_major_config(struct phylink *pl, bool restart, */ static int phylink_change_inband_advert(struct phylink *pl) { - unsigned int neg_mode; int ret; if (test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) @@ -1479,15 +1350,11 @@ static int phylink_change_inband_advert(struct phylink *pl) phylink_pcs_neg_mode(pl, pl->pcs, pl->link_config.interface, pl->link_config.advertising); - neg_mode = pl->act_link_an_mode; - if (pl->pcs->neg_mode) - neg_mode = pl->pcs_neg_mode; - /* Modern PCS-based method; update the advert at the PCS, and * restart negotiation if the pcs_config() helper indicates that * the programmed advertisement has changed. */ - ret = phylink_pcs_config(pl->pcs, neg_mode, &pl->link_config, + ret = phylink_pcs_config(pl->pcs, pl->pcs_neg_mode, &pl->link_config, !!(pl->link_config.pause & MLO_PAUSE_AN)); if (ret < 0) return ret; @@ -1511,13 +1378,7 @@ static void phylink_mac_pcs_get_state(struct phylink *pl, 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); - + autoneg = pl->pcs_neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED; if (autoneg) { state->speed = SPEED_UNKNOWN; state->duplex = DUPLEX_UNKNOWN; @@ -1528,6 +1389,7 @@ static void phylink_mac_pcs_get_state(struct phylink *pl, state->pause = pl->link_config.pause; } + pcs = pl->pcs; if (pcs) pcs->ops->pcs_get_state(pcs, pl->pcs_neg_mode, state); else @@ -1601,6 +1463,8 @@ static void phylink_deactivate_lpi(struct phylink *pl) phylink_dbg(pl, "disabling LPI\n"); pl->mac_ops->mac_disable_tx_lpi(pl->config); + + phylink_pcs_disable_eee(pl->pcs); } } @@ -1617,20 +1481,24 @@ static void phylink_activate_lpi(struct phylink *pl) phylink_dbg(pl, "LPI timer %uus, tx clock stop %u\n", pl->mac_tx_lpi_timer, pl->mac_tx_clk_stop); + phylink_pcs_enable_eee(pl->pcs); + err = pl->mac_ops->mac_enable_tx_lpi(pl->config, pl->mac_tx_lpi_timer, pl->mac_tx_clk_stop); - if (!err) - pl->mac_enable_tx_lpi = true; - else + if (err) { + phylink_pcs_disable_eee(pl->pcs); phylink_err(pl, "%ps() failed: %pe\n", pl->mac_ops->mac_enable_tx_lpi, ERR_PTR(err)); + return; + } + + pl->mac_enable_tx_lpi = true; } static void phylink_link_up(struct phylink *pl, struct phylink_link_state link_state) { struct net_device *ndev = pl->netdev; - unsigned int neg_mode; int speed, duplex; bool rx_pause; @@ -1661,11 +1529,7 @@ static void phylink_link_up(struct phylink *pl, pl->cur_interface = link_state.interface; - 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, + phylink_pcs_link_up(pl->pcs, pl->pcs_neg_mode, pl->cur_interface, speed, duplex); pl->mac_ops->mac_link_up(pl->config, pl->phydev, pl->act_link_an_mode, @@ -1795,6 +1659,12 @@ static void phylink_resolve(struct work_struct *w) } } + /* If configuration of the interface failed, force the link down + * until we get a successful configuration. + */ + if (pl->major_config_failed) + link_state.link = false; + if (link_state.link != cur_link_state) { pl->old_link_state = link_state.link; if (!link_state.link) @@ -1879,21 +1749,20 @@ static int phylink_register_sfp(struct phylink *pl, int phylink_set_fixed_link(struct phylink *pl, const struct phylink_link_state *state) { - const struct phy_setting *s; + const struct link_capabilities *c; 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) + c = phy_caps_lookup(state->speed, state->duplex, + pl->supported, true); + if (!c) return -EINVAL; adv = pl->link_config.advertising; - linkmode_zero(adv); - linkmode_set_bit(s->bit, adv); + linkmode_and(adv, pl->supported, c->linkmodes); linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, adv); pl->link_config.speed = state->speed; @@ -1957,8 +1826,7 @@ struct phylink *phylink_create(struct phylink_config *config, return ERR_PTR(-EINVAL); } - pl->mac_supports_eee_ops = mac_ops->mac_disable_tx_lpi && - mac_ops->mac_enable_tx_lpi; + 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); @@ -2046,7 +1914,7 @@ bool phylink_expects_phy(struct phylink *pl) { if (pl->cfg_link_an_mode == MLO_AN_FIXED || (pl->cfg_link_an_mode == MLO_AN_INBAND && - phy_interface_mode_is_8023z(pl->link_config.interface))) + phy_interface_mode_is_8023z(pl->link_interface))) return false; return true; } @@ -2595,6 +2463,64 @@ void phylink_stop(struct phylink *pl) EXPORT_SYMBOL_GPL(phylink_stop); /** + * phylink_rx_clk_stop_block() - block PHY ability to stop receive clock in LPI + * @pl: a pointer to a &struct phylink returned from phylink_create() + * + * Disable the PHY's ability to stop the receive clock while the receive path + * is in EEE LPI state, until the number of calls to phylink_rx_clk_stop_block() + * are balanced by calls to phylink_rx_clk_stop_unblock(). + */ +void phylink_rx_clk_stop_block(struct phylink *pl) +{ + ASSERT_RTNL(); + + if (pl->mac_rx_clk_stop_blocked == U8_MAX) { + phylink_warn(pl, "%s called too many times - ignoring\n", + __func__); + dump_stack(); + return; + } + + /* Disable PHY receive clock stop if this is the first time this + * function has been called and clock-stop was previously enabled. + */ + if (pl->mac_rx_clk_stop_blocked++ == 0 && + pl->mac_supports_eee_ops && pl->phydev && + pl->config->eee_rx_clk_stop_enable) + phy_eee_rx_clock_stop(pl->phydev, false); +} +EXPORT_SYMBOL_GPL(phylink_rx_clk_stop_block); + +/** + * phylink_rx_clk_stop_unblock() - unblock PHY ability to stop receive clock + * @pl: a pointer to a &struct phylink returned from phylink_create() + * + * All calls to phylink_rx_clk_stop_block() must be balanced with a + * corresponding call to phylink_rx_clk_stop_unblock() to restore the PHYs + * ability to stop the receive clock when the receive path is in EEE LPI mode. + */ +void phylink_rx_clk_stop_unblock(struct phylink *pl) +{ + ASSERT_RTNL(); + + if (pl->mac_rx_clk_stop_blocked == 0) { + phylink_warn(pl, "%s called too many times - ignoring\n", + __func__); + dump_stack(); + return; + } + + /* Re-enable PHY receive clock stop if the number of unblocks matches + * the number of calls to the block function above. + */ + if (--pl->mac_rx_clk_stop_blocked == 0 && + pl->mac_supports_eee_ops && pl->phydev && + pl->config->eee_rx_clk_stop_enable) + phy_eee_rx_clock_stop(pl->phydev, true); +} +EXPORT_SYMBOL_GPL(phylink_rx_clk_stop_unblock); + +/** * phylink_suspend() - handle a network device suspend event * @pl: a pointer to a &struct phylink returned from phylink_create() * @mac_wol: true if the MAC needs to receive packets for Wake-on-Lan @@ -2639,6 +2565,31 @@ 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, but if called 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) +{ + struct phy_device *phydev = pl->phydev; + + ASSERT_RTNL(); + + /* IEEE 802.3 22.2.4.1.5 allows PHYs to stop their receive clock + * when PDOWN is set. However, some MACs require RXC to be running + * in order to resume. If the MAC requires RXC, and we have a PHY, + * then resume the PHY. Note that 802.3 allows PHYs 500ms before + * the clock meets requirements. We do not implement this delay. + */ + if (pl->config->mac_requires_rxc && phydev && phydev->suspended) + phy_resume(phydev); +} +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() * @@ -2854,8 +2805,8 @@ int phylink_ethtool_ksettings_set(struct phylink *pl, const struct ethtool_link_ksettings *kset) { __ETHTOOL_DECLARE_LINK_MODE_MASK(support); + const struct link_capabilities *c; struct phylink_link_state config; - const struct phy_setting *s; ASSERT_RTNL(); @@ -2898,23 +2849,23 @@ int phylink_ethtool_ksettings_set(struct phylink *pl, /* Autonegotiation disabled, select a suitable speed and * duplex. */ - s = phy_lookup_setting(kset->base.speed, kset->base.duplex, - pl->supported, false); - if (!s) + c = phy_caps_lookup(kset->base.speed, kset->base.duplex, + pl->supported, false); + if (!c) return -EINVAL; /* If we have a fixed link, refuse to change link parameters. * If the link parameters match, accept them but do nothing. */ if (pl->req_link_an_mode == MLO_AN_FIXED) { - if (s->speed != pl->link_config.speed || - s->duplex != pl->link_config.duplex) + if (c->speed != pl->link_config.speed || + c->duplex != pl->link_config.duplex) return -EINVAL; return 0; } - config.speed = s->speed; - config.duplex = s->duplex; + config.speed = c->speed; + config.duplex = c->duplex; break; case AUTONEG_ENABLE: @@ -3160,24 +3111,6 @@ int phylink_get_eee_err(struct phylink *pl) EXPORT_SYMBOL_GPL(phylink_get_eee_err); /** - * phylink_init_eee() - init and check the EEE features - * @pl: a pointer to a &struct phylink returned from phylink_create() - * @clk_stop_enable: allow PHY to stop receive clock - * - * Must be called either with RTNL held or within mac_link_up() - */ -int phylink_init_eee(struct phylink *pl, bool clk_stop_enable) -{ - int ret = -EOPNOTSUPP; - - if (pl->phydev) - ret = phy_init_eee(pl->phydev, clk_stop_enable); - - return ret; -} -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_keee for the read parameters |