X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=blobdiff_plain;f=drivers%2Fnet%2Fphy%2Fphylink.c;h=88686e0f9ae14d37b1cbe3fa53f3eb562d23011a;hb=3a68ba6fbab786a50f380894c832bf36ba7335f3;hp=9a616d6bc4eb22dfd484a69f48b12ba077eca393;hpb=7ada90eb9c7ae4a8eb066f8e9b4d80122f0363db;p=linux.git diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 9a616d6bc4eb..88686e0f9ae1 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -48,7 +48,8 @@ struct phylink { unsigned long phylink_disable_state; /* bitmask of disables */ struct phy_device *phydev; phy_interface_t link_interface; /* PHY_INTERFACE_xxx */ - u8 link_an_mode; /* MLO_AN_xxx */ + u8 cfg_link_an_mode; /* MLO_AN_xxx */ + u8 cur_link_an_mode; u8 link_port; /* The current non-phy ethtool port */ __ETHTOOL_DECLARE_LINK_MODE_MASK(supported); @@ -71,6 +72,9 @@ struct phylink { bool mac_link_dropped; struct sfp_bus *sfp_bus; + bool sfp_may_have_phy; + __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support); + u8 sfp_port; }; #define phylink_printk(level, pl, fmt, ...) \ @@ -182,8 +186,8 @@ static int phylink_parse_fixedlink(struct phylink *pl, pl->link_config.pause |= MLO_PAUSE_ASYM; if (ret == 0) { - desc = fwnode_get_named_gpiod(fixed_node, "link-gpios", - 0, GPIOD_IN, "?"); + desc = fwnode_gpiod_get_index(fixed_node, "link", 0, + GPIOD_IN, "?"); if (!IS_ERR(desc)) pl->link_gpio = desc; @@ -256,12 +260,12 @@ static int phylink_parse_mode(struct phylink *pl, struct fwnode_handle *fwnode) dn = fwnode_get_named_child_node(fwnode, "fixed-link"); if (dn || fwnode_property_present(fwnode, "fixed-link")) - pl->link_an_mode = MLO_AN_FIXED; + 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) { - if (pl->link_an_mode == MLO_AN_FIXED) { + 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; @@ -273,10 +277,11 @@ static int phylink_parse_mode(struct phylink *pl, struct fwnode_handle *fwnode) phylink_set(pl->supported, Asym_Pause); phylink_set(pl->supported, Pause); pl->link_config.an_enabled = true; - pl->link_an_mode = MLO_AN_INBAND; + pl->cfg_link_an_mode = MLO_AN_INBAND; switch (pl->link_config.interface) { case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_QSGMII: phylink_set(pl->supported, 10baseT_Half); phylink_set(pl->supported, 10baseT_Full); phylink_set(pl->supported, 100baseT_Half); @@ -294,6 +299,7 @@ static int phylink_parse_mode(struct phylink *pl, struct fwnode_handle *fwnode) break; case PHY_INTERFACE_MODE_10GKR: + case PHY_INTERFACE_MODE_10GBASER: phylink_set(pl->supported, 10baseT_Half); phylink_set(pl->supported, 10baseT_Full); phylink_set(pl->supported, 100baseT_Half); @@ -333,14 +339,14 @@ static void phylink_mac_config(struct phylink *pl, { phylink_dbg(pl, "%s: mode=%s/%s/%s/%s adv=%*pb pause=%02x link=%u an=%u\n", - __func__, phylink_an_mode_str(pl->link_an_mode), + __func__, phylink_an_mode_str(pl->cur_link_an_mode), phy_modes(state->interface), phy_speed_to_str(state->speed), phy_duplex_to_str(state->duplex), __ETHTOOL_LINK_MODE_MASK_NBITS, state->advertising, state->pause, state->link, state->an_enabled); - pl->ops->mac_config(pl->config, pl->link_an_mode, state); + pl->ops->mac_config(pl->config, pl->cur_link_an_mode, state); } static void phylink_mac_config_up(struct phylink *pl, @@ -441,9 +447,8 @@ static void phylink_mac_link_up(struct phylink *pl, struct net_device *ndev = pl->netdev; pl->cur_interface = link_state.interface; - pl->ops->mac_link_up(pl->config, pl->link_an_mode, - pl->phy_state.interface, - pl->phydev); + pl->ops->mac_link_up(pl->config, pl->cur_link_an_mode, + pl->cur_interface, pl->phydev); if (ndev) netif_carrier_on(ndev); @@ -461,7 +466,7 @@ static void phylink_mac_link_down(struct phylink *pl) if (ndev) netif_carrier_off(ndev); - pl->ops->mac_link_down(pl->config, pl->link_an_mode, + pl->ops->mac_link_down(pl->config, pl->cur_link_an_mode, pl->cur_interface); phylink_info(pl, "Link is Down\n"); } @@ -480,7 +485,7 @@ static void phylink_resolve(struct work_struct *w) } else if (pl->mac_link_dropped) { link_state.link = false; } else { - switch (pl->link_an_mode) { + switch (pl->cur_link_an_mode) { case MLO_AN_PHY: link_state = pl->phy_state; phylink_resolve_flow(pl, &link_state); @@ -648,7 +653,7 @@ struct phylink *phylink_create(struct phylink_config *config, return ERR_PTR(ret); } - if (pl->link_an_mode == MLO_AN_FIXED) { + if (pl->cfg_link_an_mode == MLO_AN_FIXED) { ret = phylink_parse_fixedlink(pl, fwnode); if (ret < 0) { kfree(pl); @@ -656,6 +661,8 @@ struct phylink *phylink_create(struct phylink_config *config, } } + pl->cur_link_an_mode = pl->cfg_link_an_mode; + ret = phylink_register_sfp(pl, fwnode); if (ret < 0) { kfree(pl); @@ -711,7 +718,8 @@ static void phylink_phy_change(struct phy_device *phydev, bool up, phy_duplex_to_str(phydev->duplex)); } -static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy) +static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy, + phy_interface_t interface) { struct phylink_link_state config; __ETHTOOL_DECLARE_LINK_MODE_MASK(supported); @@ -729,7 +737,19 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy) memset(&config, 0, sizeof(config)); linkmode_copy(supported, phy->supported); linkmode_copy(config.advertising, phy->advertising); - config.interface = pl->link_config.interface; + + /* Clause 45 PHYs switch their Serdes lane between several different + * modes, normally 10GBASE-R, SGMII. Some use 2500BASE-X for 2.5G + * speeds. We really need to know which interface modes the PHY and + * MAC supports to properly work out which linkmodes can be supported. + */ + if (phy->is_c45 && + interface != PHY_INTERFACE_MODE_RXAUI && + interface != PHY_INTERFACE_MODE_XAUI && + interface != PHY_INTERFACE_MODE_USXGMII) + config.interface = PHY_INTERFACE_MODE_NA; + else + config.interface = interface; ret = phylink_validate(pl, supported, &config); if (ret) @@ -745,6 +765,7 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy) mutex_lock(&phy->lock); mutex_lock(&pl->state_mutex); pl->phydev = phy; + pl->phy_state.interface = interface; linkmode_copy(pl->supported, supported); linkmode_copy(pl->link_config.advertising, config.advertising); @@ -764,28 +785,18 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy) return 0; } -static int __phylink_connect_phy(struct phylink *pl, struct phy_device *phy, - phy_interface_t interface) +static int phylink_attach_phy(struct phylink *pl, struct phy_device *phy, + phy_interface_t interface) { - int ret; - - if (WARN_ON(pl->link_an_mode == MLO_AN_FIXED || - (pl->link_an_mode == MLO_AN_INBAND && + 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)))) return -EINVAL; if (pl->phydev) return -EBUSY; - ret = phy_attach_direct(pl->netdev, phy, 0, interface); - if (ret) - return ret; - - ret = phylink_bringup_phy(pl, phy); - if (ret) - phy_detach(phy); - - return ret; + return phy_attach_direct(pl->netdev, phy, 0, interface); } /** @@ -805,13 +816,23 @@ static int __phylink_connect_phy(struct phylink *pl, struct phy_device *phy, */ int phylink_connect_phy(struct phylink *pl, struct phy_device *phy) { + int ret; + /* Use PHY device/driver interface */ if (pl->link_interface == PHY_INTERFACE_MODE_NA) { pl->link_interface = phy->interface; pl->link_config.interface = pl->link_interface; } - return __phylink_connect_phy(pl, phy, pl->link_interface); + ret = phylink_attach_phy(pl, phy, pl->link_interface); + if (ret < 0) + return ret; + + ret = phylink_bringup_phy(pl, phy, pl->link_config.interface); + if (ret) + phy_detach(phy); + + return ret; } EXPORT_SYMBOL_GPL(phylink_connect_phy); @@ -835,8 +856,8 @@ int phylink_of_phy_connect(struct phylink *pl, struct device_node *dn, int ret; /* Fixed links and 802.3z are handled without needing a PHY */ - if (pl->link_an_mode == MLO_AN_FIXED || - (pl->link_an_mode == MLO_AN_INBAND && + 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; @@ -847,20 +868,23 @@ int phylink_of_phy_connect(struct phylink *pl, struct device_node *dn, phy_node = of_parse_phandle(dn, "phy-device", 0); if (!phy_node) { - if (pl->link_an_mode == MLO_AN_PHY) + if (pl->cfg_link_an_mode == MLO_AN_PHY) return -ENODEV; return 0; } - phy_dev = of_phy_attach(pl->netdev, phy_node, flags, - pl->link_interface); + phy_dev = of_phy_find_device(phy_node); /* We're done with the phy_node handle */ of_node_put(phy_node); - if (!phy_dev) return -ENODEV; - ret = phylink_bringup_phy(pl, phy_dev); + ret = phy_attach_direct(pl->netdev, phy_dev, flags, + pl->link_interface); + if (ret) + return ret; + + ret = phylink_bringup_phy(pl, phy_dev, pl->link_config.interface); if (ret) phy_detach(phy_dev); @@ -910,7 +934,7 @@ int phylink_fixed_state_cb(struct phylink *pl, /* It does not make sense to let the link be overriden unless we use * MLO_AN_FIXED */ - if (pl->link_an_mode != MLO_AN_FIXED) + if (pl->cfg_link_an_mode != MLO_AN_FIXED) return -EINVAL; mutex_lock(&pl->state_mutex); @@ -960,7 +984,7 @@ void phylink_start(struct phylink *pl) ASSERT_RTNL(); phylink_info(pl, "configuring for %s/%s link mode\n", - phylink_an_mode_str(pl->link_an_mode), + phylink_an_mode_str(pl->cur_link_an_mode), phy_modes(pl->link_config.interface)); /* Always set the carrier off */ @@ -983,7 +1007,7 @@ void phylink_start(struct phylink *pl) clear_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state); phylink_run_resolve(pl); - if (pl->link_an_mode == MLO_AN_FIXED && 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) { @@ -998,7 +1022,7 @@ void phylink_start(struct phylink *pl) if (irq <= 0) mod_timer(&pl->link_poll, jiffies + HZ); } - if (pl->link_an_mode == MLO_AN_FIXED && pl->get_fixed_state) + if (pl->cfg_link_an_mode == MLO_AN_FIXED && pl->get_fixed_state) mod_timer(&pl->link_poll, jiffies + HZ); if (pl->phydev) phy_start(pl->phydev); @@ -1125,7 +1149,7 @@ int phylink_ethtool_ksettings_get(struct phylink *pl, linkmode_copy(kset->link_modes.supported, pl->supported); - switch (pl->link_an_mode) { + switch (pl->cur_link_an_mode) { case MLO_AN_FIXED: /* We are using fixed settings. Report these as the * current link settings - and note that these also @@ -1197,7 +1221,7 @@ int phylink_ethtool_ksettings_set(struct phylink *pl, /* If we have a fixed link (as specified by firmware), refuse * to change link parameters. */ - if (pl->link_an_mode == MLO_AN_FIXED && + if (pl->cur_link_an_mode == MLO_AN_FIXED && (s->speed != pl->link_config.speed || s->duplex != pl->link_config.duplex)) return -EINVAL; @@ -1209,7 +1233,7 @@ int phylink_ethtool_ksettings_set(struct phylink *pl, __clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, config.advertising); } else { /* If we have a fixed link, refuse to enable autonegotiation */ - if (pl->link_an_mode == MLO_AN_FIXED) + if (pl->cur_link_an_mode == MLO_AN_FIXED) return -EINVAL; config.speed = SPEED_UNKNOWN; @@ -1219,44 +1243,66 @@ int phylink_ethtool_ksettings_set(struct phylink *pl, __set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, config.advertising); } - if (phylink_validate(pl, support, &config)) - return -EINVAL; - - /* If autonegotiation is enabled, we must have an advertisement */ - if (config.an_enabled && phylink_is_empty_linkmode(config.advertising)) - return -EINVAL; - - our_kset = *kset; - linkmode_copy(our_kset.link_modes.advertising, config.advertising); - our_kset.base.speed = config.speed; - our_kset.base.duplex = config.duplex; - - /* If we have a PHY, configure the phy */ if (pl->phydev) { + /* If we have a PHY, we process the kset change via phylib. + * phylib will call our link state function if the PHY + * parameters have changed, which will trigger a resolve + * and update the MAC configuration. + */ + our_kset = *kset; + linkmode_copy(our_kset.link_modes.advertising, + config.advertising); + our_kset.base.speed = config.speed; + our_kset.base.duplex = config.duplex; + ret = phy_ethtool_ksettings_set(pl->phydev, &our_kset); if (ret) return ret; - } - mutex_lock(&pl->state_mutex); - /* Configure the MAC to match the new settings */ - linkmode_copy(pl->link_config.advertising, our_kset.link_modes.advertising); - pl->link_config.interface = config.interface; - pl->link_config.speed = our_kset.base.speed; - pl->link_config.duplex = our_kset.base.duplex; - pl->link_config.an_enabled = our_kset.base.autoneg != AUTONEG_DISABLE; + mutex_lock(&pl->state_mutex); + /* Save the new configuration */ + linkmode_copy(pl->link_config.advertising, + our_kset.link_modes.advertising); + pl->link_config.interface = config.interface; + pl->link_config.speed = our_kset.base.speed; + pl->link_config.duplex = our_kset.base.duplex; + pl->link_config.an_enabled = our_kset.base.autoneg != + AUTONEG_DISABLE; + mutex_unlock(&pl->state_mutex); + } else { + /* For a fixed link, this isn't able to change any parameters, + * which just leaves inband mode. + */ + if (phylink_validate(pl, support, &config)) + return -EINVAL; - /* If we have a PHY, phylib will call our link state function if the - * mode has changed, which will trigger a resolve and update the MAC - * configuration. For a fixed link, this isn't able to change any - * parameters, which just leaves inband mode. - */ - if (pl->link_an_mode == MLO_AN_INBAND && - !test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) { - phylink_mac_config(pl, &pl->link_config); - phylink_mac_an_restart(pl); + /* If autonegotiation is enabled, we must have an advertisement */ + if (config.an_enabled && + phylink_is_empty_linkmode(config.advertising)) + return -EINVAL; + + mutex_lock(&pl->state_mutex); + linkmode_copy(pl->link_config.advertising, config.advertising); + pl->link_config.interface = config.interface; + pl->link_config.speed = config.speed; + pl->link_config.duplex = config.duplex; + pl->link_config.an_enabled = kset->base.autoneg != + AUTONEG_DISABLE; + + if (pl->cur_link_an_mode == MLO_AN_INBAND && + !test_bit(PHYLINK_DISABLE_STOPPED, + &pl->phylink_disable_state)) { + /* If in 802.3z mode, this updates the advertisement. + * + * If we are in SGMII mode without a PHY, there is no + * advertisement; the only thing we have is the pause + * modes which can only come from a PHY. + */ + phylink_mac_config(pl, &pl->link_config); + phylink_mac_an_restart(pl); + } + mutex_unlock(&pl->state_mutex); } - mutex_unlock(&pl->state_mutex); return 0; } @@ -1341,7 +1387,7 @@ int phylink_ethtool_set_pauseparam(struct phylink *pl, pause->tx_pause); } else if (!test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) { - switch (pl->link_an_mode) { + switch (pl->cur_link_an_mode) { case MLO_AN_FIXED: /* Should we allow fixed links to change against the config? */ phylink_resolve_flow(pl, config); @@ -1548,7 +1594,7 @@ static int phylink_mii_read(struct phylink *pl, unsigned int phy_id, struct phylink_link_state state; int val = 0xffff; - switch (pl->link_an_mode) { + switch (pl->cur_link_an_mode) { case MLO_AN_FIXED: if (phy_id == 0) { phylink_get_fixed_state(pl, &state); @@ -1573,7 +1619,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->link_an_mode) { + switch (pl->cur_link_an_mode) { case MLO_AN_FIXED: break; @@ -1679,25 +1725,21 @@ static void phylink_sfp_detach(void *upstream, struct sfp_bus *bus) pl->netdev->sfp_bus = NULL; } -static int phylink_sfp_module_insert(void *upstream, - const struct sfp_eeprom_id *id) +static int phylink_sfp_config(struct phylink *pl, u8 mode, + const unsigned long *supported, + const unsigned long *advertising) { - struct phylink *pl = upstream; - __ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, }; __ETHTOOL_DECLARE_LINK_MODE_MASK(support1); + __ETHTOOL_DECLARE_LINK_MODE_MASK(support); struct phylink_link_state config; phy_interface_t iface; - int ret = 0; bool changed; - u8 port; - - ASSERT_RTNL(); + int ret; - sfp_parse_support(pl->sfp_bus, id, support); - port = sfp_parse_port(pl->sfp_bus, id, support); + linkmode_copy(support, supported); memset(&config, 0, sizeof(config)); - linkmode_copy(config.advertising, support); + linkmode_copy(config.advertising, advertising); config.interface = PHY_INTERFACE_MODE_NA; config.speed = SPEED_UNKNOWN; config.duplex = DUPLEX_UNKNOWN; @@ -1712,9 +1754,7 @@ static int phylink_sfp_module_insert(void *upstream, return ret; } - linkmode_copy(support1, support); - - iface = sfp_select_interface(pl->sfp_bus, id, config.advertising); + 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", @@ -1723,18 +1763,18 @@ static int phylink_sfp_module_insert(void *upstream, } config.interface = iface; + linkmode_copy(support1, support); ret = phylink_validate(pl, support1, &config); if (ret) { phylink_err(pl, "validation of %s/%s with support %*pb failed: %d\n", - phylink_an_mode_str(MLO_AN_INBAND), + phylink_an_mode_str(mode), phy_modes(config.interface), __ETHTOOL_LINK_MODE_MASK_NBITS, support, ret); return ret; } phylink_dbg(pl, "requesting link mode %s/%s with support %*pb\n", - phylink_an_mode_str(MLO_AN_INBAND), - phy_modes(config.interface), + phylink_an_mode_str(mode), phy_modes(config.interface), __ETHTOOL_LINK_MODE_MASK_NBITS, support); if (phy_interface_mode_is_8023z(iface) && pl->phydev) @@ -1746,19 +1786,19 @@ static int phylink_sfp_module_insert(void *upstream, linkmode_copy(pl->link_config.advertising, config.advertising); } - if (pl->link_an_mode != MLO_AN_INBAND || + if (pl->cur_link_an_mode != mode || pl->link_config.interface != config.interface) { pl->link_config.interface = config.interface; - pl->link_an_mode = MLO_AN_INBAND; + pl->cur_link_an_mode = mode; changed = true; phylink_info(pl, "switched to %s/%s link mode\n", - phylink_an_mode_str(MLO_AN_INBAND), + phylink_an_mode_str(mode), phy_modes(config.interface)); } - pl->link_port = port; + pl->link_port = pl->sfp_port; if (changed && !test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) @@ -1767,6 +1807,55 @@ static int phylink_sfp_module_insert(void *upstream, return ret; } +static int phylink_sfp_module_insert(void *upstream, + const struct sfp_eeprom_id *id) +{ + struct phylink *pl = upstream; + unsigned long *support = pl->sfp_support; + + ASSERT_RTNL(); + + linkmode_zero(support); + sfp_parse_support(pl->sfp_bus, id, support); + pl->sfp_port = sfp_parse_port(pl->sfp_bus, id, support); + + /* 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(pl, MLO_AN_INBAND, support, support); +} + +static int phylink_sfp_module_start(void *upstream) +{ + struct phylink *pl = upstream; + + /* If this SFP module has a PHY, start the PHY now. */ + if (pl->phydev) { + phy_start(pl->phydev); + return 0; + } + + /* If the module may have a PHY but we didn't detect one we + * need to configure the MAC here. + */ + if (!pl->sfp_may_have_phy) + return 0; + + return phylink_sfp_config(pl, MLO_AN_INBAND, + pl->sfp_support, pl->sfp_support); +} + +static void phylink_sfp_module_stop(void *upstream) +{ + struct phylink *pl = upstream; + + /* If this SFP module has a PHY, stop it. */ + if (pl->phydev) + phy_stop(pl->phydev); +} + static void phylink_sfp_link_down(void *upstream) { struct phylink *pl = upstream; @@ -1786,11 +1875,51 @@ static void phylink_sfp_link_up(void *upstream) phylink_run_resolve(pl); } +/* The Broadcom BCM84881 in the Methode DM7052 is unable to provide a SGMII + * or 802.3z control word, so inband will not work. + */ +static bool phylink_phy_no_inband(struct phy_device *phy) +{ + return phy->is_c45 && + (phy->c45_ids.device_ids[1] & 0xfffffff0) == 0xae025150; +} + static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy) { struct phylink *pl = upstream; + phy_interface_t interface; + u8 mode; + int ret; - return __phylink_connect_phy(upstream, phy, pl->link_config.interface); + /* + * This is the new way of dealing with flow control for PHYs, + * as described by Timur Tabi in commit 529ed1275263 ("net: phy: + * phy drivers should not set SUPPORTED_[Asym_]Pause") except + * using our validate call to the MAC, we rely upon the MAC + * clearing the bits from both supported and advertising fields. + */ + phy_support_asym_pause(phy); + + if (phylink_phy_no_inband(phy)) + mode = MLO_AN_PHY; + else + mode = MLO_AN_INBAND; + + /* Do the initial configuration */ + ret = phylink_sfp_config(pl, mode, phy->supported, phy->advertising); + if (ret < 0) + return ret; + + interface = pl->link_config.interface; + ret = phylink_attach_phy(pl, phy, interface); + if (ret < 0) + return ret; + + ret = phylink_bringup_phy(pl, phy, interface); + if (ret) + phy_detach(phy); + + return ret; } static void phylink_sfp_disconnect_phy(void *upstream) @@ -1802,6 +1931,8 @@ static const struct sfp_upstream_ops sfp_phylink_ops = { .attach = phylink_sfp_attach, .detach = phylink_sfp_detach, .module_insert = phylink_sfp_module_insert, + .module_start = phylink_sfp_module_start, + .module_stop = phylink_sfp_module_stop, .link_up = phylink_sfp_link_up, .link_down = phylink_sfp_link_down, .connect_phy = phylink_sfp_connect_phy,