Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
[linux-2.6-microblaze.git] / drivers / net / phy / phylink.c
index 1585eeb..ba9468c 100644 (file)
@@ -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, ...) \
@@ -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,7 +277,7 @@ 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:
@@ -333,14 +337,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,7 +445,7 @@ 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->ops->mac_link_up(pl->config, pl->cur_link_an_mode,
                             pl->cur_interface, pl->phydev);
 
        if (ndev)
@@ -460,7 +464,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");
 }
@@ -479,7 +483,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);
@@ -647,7 +651,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);
@@ -655,6 +659,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);
@@ -710,7 +716,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);
@@ -728,7 +735,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)
@@ -744,6 +763,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);
 
@@ -763,28 +783,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);
 }
 
 /**
@@ -804,13 +814,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);
 
@@ -834,8 +854,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;
 
@@ -846,20 +866,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);
 
@@ -909,7 +932,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);
@@ -959,7 +982,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 */
@@ -982,7 +1005,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) {
@@ -997,7 +1020,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);
@@ -1124,7 +1147,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
@@ -1196,7 +1219,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;
@@ -1208,7 +1231,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;
@@ -1218,44 +1241,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;
 }
@@ -1340,7 +1385,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);
@@ -1547,7 +1592,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);
@@ -1572,7 +1617,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;
 
@@ -1678,25 +1723,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;
@@ -1711,9 +1752,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",
@@ -1722,18 +1761,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)
@@ -1745,19 +1784,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))
@@ -1766,6 +1805,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;
@@ -1785,11 +1873,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)
@@ -1801,6 +1929,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,