diff options
Diffstat (limited to 'drivers/net/phy/phylink.c')
-rw-r--r-- | drivers/net/phy/phylink.c | 219 |
1 files changed, 167 insertions, 52 deletions
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 0faa3d97e06b..9d7799ea1c17 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -67,6 +67,8 @@ struct phylink { struct timer_list link_poll; struct mutex state_mutex; + /* Serialize updates to pl->phydev with phylink_resolve() */ + struct mutex phydev_mutex; struct phylink_link_state phy_state; unsigned int phy_ib_mode; struct work_struct resolve; @@ -127,6 +129,9 @@ do { \ #endif static const phy_interface_t phylink_sfp_interface_preference[] = { + PHY_INTERFACE_MODE_100GBASEP, + PHY_INTERFACE_MODE_50GBASER, + PHY_INTERFACE_MODE_LAUI, PHY_INTERFACE_MODE_25GBASER, PHY_INTERFACE_MODE_USXGMII, PHY_INTERFACE_MODE_10GBASER, @@ -234,6 +239,7 @@ static int phylink_interface_max_speed(phy_interface_t interface) case PHY_INTERFACE_MODE_SMII: case PHY_INTERFACE_MODE_REVMII: case PHY_INTERFACE_MODE_MII: + case PHY_INTERFACE_MODE_MIILITE: return SPEED_100; case PHY_INTERFACE_MODE_TBI: @@ -274,6 +280,13 @@ static int phylink_interface_max_speed(phy_interface_t interface) case PHY_INTERFACE_MODE_XLGMII: return SPEED_40000; + case PHY_INTERFACE_MODE_50GBASER: + case PHY_INTERFACE_MODE_LAUI: + return SPEED_50000; + + case PHY_INTERFACE_MODE_100GBASEP: + return SPEED_100000; + case PHY_INTERFACE_MODE_INTERNAL: case PHY_INTERFACE_MODE_NA: case PHY_INTERFACE_MODE_MAX: @@ -689,6 +702,9 @@ static int phylink_parse_fixedlink(struct phylink *pl, return -EINVAL; } + phylink_warn(pl, "%pfw uses deprecated array-style fixed-link binding!\n", + fwnode); + ret = fwnode_property_read_u32_array(fwnode, "fixed-link", prop, ARRAY_SIZE(prop)); if (!ret) { @@ -798,6 +814,9 @@ static int phylink_parse_mode(struct phylink *pl, case PHY_INTERFACE_MODE_10GKR: case PHY_INTERFACE_MODE_10GBASER: case PHY_INTERFACE_MODE_XLGMII: + case PHY_INTERFACE_MODE_50GBASER: + case PHY_INTERFACE_MODE_LAUI: + case PHY_INTERFACE_MODE_100GBASEP: caps = ~(MAC_SYM_PAUSE | MAC_ASYM_PAUSE); caps = phylink_get_capabilities(pl->link_config.interface, caps, RATE_MATCH_NONE); @@ -1002,6 +1021,42 @@ static void phylink_pcs_an_restart(struct phylink *pl) pl->pcs->ops->pcs_an_restart(pl->pcs); } +enum inband_type { + INBAND_NONE, + INBAND_CISCO_SGMII, + INBAND_BASEX, +}; + +static enum inband_type phylink_get_inband_type(phy_interface_t interface) +{ + 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 + * with SGMII but do not send the inband data. + */ + return INBAND_CISCO_SGMII; + + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_2500BASEX: + /* 1000base-X is designed for use media-side for Fibre + * connections, and thus the Autoneg bit needs to be + * taken into account. We also do this for 2500base-X + * as well, but drivers may not support this, so may + * need to override this. + */ + return INBAND_BASEX; + + default: + return INBAND_NONE; + } +} + /** * phylink_pcs_neg_mode() - helper to determine PCS inband mode * @pl: a pointer to a &struct phylink returned from phylink_create() @@ -1029,46 +1084,19 @@ static void phylink_pcs_neg_mode(struct phylink *pl, struct phylink_pcs *pcs, unsigned int pcs_ib_caps = 0; unsigned int phy_ib_caps = 0; unsigned int neg_mode, mode; - enum { - INBAND_CISCO_SGMII, - 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 - * with SGMII but do not send the inband data. - */ - type = INBAND_CISCO_SGMII; - break; + enum inband_type type; - case PHY_INTERFACE_MODE_1000BASEX: - case PHY_INTERFACE_MODE_2500BASEX: - /* 1000base-X is designed for use media-side for Fibre - * connections, and thus the Autoneg bit needs to be - * taken into account. We also do this for 2500base-X - * as well, but drivers may not support this, so may - * need to override this. - */ - type = INBAND_BASEX; - break; - - default: + type = phylink_get_inband_type(interface); + if (type == INBAND_NONE) { pl->pcs_neg_mode = PHYLINK_PCS_NEG_NONE; - pl->act_link_an_mode = mode; + pl->act_link_an_mode = pl->req_link_an_mode; return; } + mode = pl->req_link_an_mode; + + pl->phy_ib_mode = 0; + if (pcs) pcs_ib_caps = phylink_pcs_inband_caps(pcs, interface); @@ -1409,6 +1437,7 @@ static void phylink_get_fixed_state(struct phylink *pl, static void phylink_mac_initial_config(struct phylink *pl, bool force_restart) { struct phylink_link_state link_state; + struct phy_device *phy = pl->phydev; switch (pl->req_link_an_mode) { case MLO_AN_PHY: @@ -1432,7 +1461,11 @@ static void phylink_mac_initial_config(struct phylink *pl, bool force_restart) link_state.link = false; phylink_apply_manual_flow(pl, &link_state); + if (phy) + mutex_lock(&phy->lock); phylink_major_config(pl, force_restart, &link_state); + if (phy) + mutex_unlock(&phy->lock); } static const char *phylink_pause_to_str(int pause) @@ -1568,8 +1601,13 @@ static void phylink_resolve(struct work_struct *w) struct phylink_link_state link_state; bool mac_config = false; bool retrigger = false; + struct phy_device *phy; bool cur_link_state; + mutex_lock(&pl->phydev_mutex); + phy = pl->phydev; + if (phy) + mutex_lock(&phy->lock); mutex_lock(&pl->state_mutex); cur_link_state = phylink_link_is_up(pl); @@ -1603,11 +1641,11 @@ static void phylink_resolve(struct work_struct *w) /* If we have a phy, the "up" state is the union of both the * PHY and the MAC */ - if (pl->phydev) + if (phy) link_state.link &= pl->phy_state.link; /* Only update if the PHY link is up */ - if (pl->phydev && pl->phy_state.link) { + if (phy && pl->phy_state.link) { /* If the interface has changed, force a link down * event if the link isn't already down, and re-resolve. */ @@ -1671,6 +1709,9 @@ static void phylink_resolve(struct work_struct *w) queue_work(system_power_efficient_wq, &pl->resolve); } mutex_unlock(&pl->state_mutex); + if (phy) + mutex_unlock(&phy->lock); + mutex_unlock(&pl->phydev_mutex); } static void phylink_run_resolve(struct phylink *pl) @@ -1806,6 +1847,7 @@ struct phylink *phylink_create(struct phylink_config *config, if (!pl) return ERR_PTR(-ENOMEM); + mutex_init(&pl->phydev_mutex); mutex_init(&pl->state_mutex); INIT_WORK(&pl->resolve, phylink_resolve); @@ -2066,6 +2108,7 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy, dev_name(&phy->mdio.dev), phy->drv->name, irq_str); kfree(irq_str); + mutex_lock(&pl->phydev_mutex); mutex_lock(&phy->lock); mutex_lock(&pl->state_mutex); pl->phydev = phy; @@ -2111,6 +2154,7 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy, mutex_unlock(&pl->state_mutex); mutex_unlock(&phy->lock); + mutex_unlock(&pl->phydev_mutex); phylink_dbg(pl, "phy: %s setting supported %*pb advertising %*pb\n", @@ -2118,9 +2162,6 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy, __ETHTOOL_LINK_MODE_MASK_NBITS, pl->supported, __ETHTOOL_LINK_MODE_MASK_NBITS, phy->advertising); - if (phy_interrupt_is_valid(phy)) - phy_request_interrupt(phy); - if (pl->config->mac_managed_pm) phy->mac_managed_pm = true; @@ -2137,6 +2178,9 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy, ret = 0; } + if (ret == 0 && phy_interrupt_is_valid(phy)) + phy_request_interrupt(phy); + return ret; } @@ -2289,6 +2333,7 @@ void phylink_disconnect_phy(struct phylink *pl) ASSERT_RTNL(); + mutex_lock(&pl->phydev_mutex); phy = pl->phydev; if (phy) { mutex_lock(&phy->lock); @@ -2298,8 +2343,11 @@ void phylink_disconnect_phy(struct phylink *pl) pl->mac_tx_clk_stop = false; mutex_unlock(&pl->state_mutex); mutex_unlock(&phy->lock); - flush_work(&pl->resolve); + } + mutex_unlock(&pl->phydev_mutex); + if (phy) { + flush_work(&pl->resolve); phy_disconnect(phy); } } @@ -2696,6 +2744,39 @@ static phy_interface_t phylink_sfp_select_interface(struct phylink *pl, return interface; } +static phy_interface_t phylink_sfp_select_interface_speed(struct phylink *pl, + u32 speed) +{ + phy_interface_t best_interface = PHY_INTERFACE_MODE_NA; + phy_interface_t interface; + u32 max_speed; + int i; + + for (i = 0; i < ARRAY_SIZE(phylink_sfp_interface_preference); i++) { + interface = phylink_sfp_interface_preference[i]; + if (!test_bit(interface, pl->sfp_interfaces)) + continue; + + max_speed = phylink_interface_max_speed(interface); + + /* The logic here is: if speed == max_speed, then we've found + * the best interface. Otherwise we find the interface that + * can just support the requested speed. + */ + if (max_speed >= speed) + best_interface = interface; + + if (max_speed <= speed) + break; + } + + if (best_interface == PHY_INTERFACE_MODE_NA) + phylink_err(pl, "selection of interface failed, speed %u\n", + speed); + + return best_interface; +} + static void phylink_merge_link_mode(unsigned long *dst, const unsigned long *b) { __ETHTOOL_DECLARE_LINK_MODE_MASK(mask); @@ -2898,8 +2979,14 @@ int phylink_ethtool_ksettings_set(struct phylink *pl, * link can be configured correctly. */ if (pl->sfp_bus) { - config.interface = phylink_sfp_select_interface(pl, + if (kset->base.autoneg == AUTONEG_ENABLE) + config.interface = + phylink_sfp_select_interface(pl, config.advertising); + else + config.interface = + phylink_sfp_select_interface_speed(pl, + config.speed); if (config.interface == PHY_INTERFACE_MODE_NA) return -EINVAL; @@ -3523,6 +3610,8 @@ static int phylink_sfp_config_phy(struct phylink *pl, struct phy_device *phy) struct phylink_link_state config; int ret; + /* We're not using pl->sfp_interfaces, so clear it. */ + phy_interface_zero(pl->sfp_interfaces); linkmode_copy(support, phy->supported); memset(&config, 0, sizeof(config)); @@ -3569,8 +3658,8 @@ static int phylink_sfp_config_phy(struct phylink *pl, struct phy_device *phy) static int phylink_sfp_config_optical(struct phylink *pl) { __ETHTOOL_DECLARE_LINK_MODE_MASK(support); - DECLARE_PHY_INTERFACE_MASK(interfaces); struct phylink_link_state config; + enum inband_type inband_type; phy_interface_t interface; int ret; @@ -3583,9 +3672,9 @@ static int phylink_sfp_config_optical(struct phylink *pl) /* Find the union of the supported interfaces by the PCS/MAC and * the SFP module. */ - phy_interface_and(interfaces, pl->config->supported_interfaces, + phy_interface_and(pl->sfp_interfaces, pl->config->supported_interfaces, pl->sfp_interfaces); - if (phy_interface_empty(interfaces)) { + if (phy_interface_empty(pl->sfp_interfaces)) { phylink_err(pl, "unsupported SFP module: no common interface modes\n"); return -EINVAL; } @@ -3601,14 +3690,14 @@ static int phylink_sfp_config_optical(struct phylink *pl) * mask to only those link modes that can be supported. */ ret = phylink_validate_mask(pl, NULL, pl->sfp_support, &config, - interfaces); + pl->sfp_interfaces); if (ret) { phylink_err(pl, "unsupported SFP module: validation with support %*pb failed\n", __ETHTOOL_LINK_MODE_MASK_NBITS, support); return ret; } - interface = phylink_choose_sfp_interface(pl, interfaces); + interface = phylink_choose_sfp_interface(pl, pl->sfp_interfaces); if (interface == PHY_INTERFACE_MODE_NA) { phylink_err(pl, "failed to select SFP interface\n"); return -EINVAL; @@ -3617,6 +3706,23 @@ static int phylink_sfp_config_optical(struct phylink *pl) phylink_dbg(pl, "optical SFP: chosen %s interface\n", phy_modes(interface)); + inband_type = phylink_get_inband_type(interface); + if (inband_type == INBAND_NONE) { + /* If this is the sole interface, and there is no inband + * support, clear the advertising mask and Autoneg bit in + * the support mask. Otherwise, just clear the Autoneg bit + * in the advertising mask. + */ + if (phy_interface_weight(pl->sfp_interfaces) == 1) { + linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, + pl->sfp_support); + linkmode_zero(config.advertising); + } else { + linkmode_clear_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"); @@ -3644,23 +3750,31 @@ static int phylink_sfp_config_optical(struct phylink *pl) static int phylink_sfp_module_insert(void *upstream, const struct sfp_eeprom_id *id) { + const struct sfp_module_caps *caps; struct phylink *pl = upstream; ASSERT_RTNL(); - linkmode_zero(pl->sfp_support); - 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); + caps = sfp_get_module_caps(pl->sfp_bus); + phy_interface_copy(pl->sfp_interfaces, caps->interfaces); + linkmode_copy(pl->sfp_support, caps->link_modes); + pl->sfp_may_have_phy = caps->may_have_phy; + pl->sfp_port = caps->port; /* 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); if (pl->sfp_may_have_phy) return 0; return phylink_sfp_config_optical(pl); } +static void phylink_sfp_module_remove(void *upstream) +{ + struct phylink *pl = upstream; + + phy_interface_zero(pl->sfp_interfaces); +} + static int phylink_sfp_module_start(void *upstream) { struct phylink *pl = upstream; @@ -3745,6 +3859,7 @@ static const struct sfp_upstream_ops sfp_phylink_ops = { .attach = phylink_sfp_attach, .detach = phylink_sfp_detach, .module_insert = phylink_sfp_module_insert, + .module_remove = phylink_sfp_module_remove, .module_start = phylink_sfp_module_start, .module_stop = phylink_sfp_module_stop, .link_up = phylink_sfp_link_up, |