|
|
@@ -1,1242 +0,0 @@
|
|
|
-From: "Russell King (Oracle)" <[email protected]>
|
|
|
-To: Andrew Lunn <[email protected]>, Heiner Kallweit <[email protected]>
|
|
|
-Cc: Alexander Couzens <[email protected]>,
|
|
|
- Andrew Lunn <[email protected]>,
|
|
|
- AngeloGioacchino Del Regno
|
|
|
- <[email protected]>,
|
|
|
- Broadcom internal kernel review list
|
|
|
- <[email protected]>,
|
|
|
- Daniel Golle <[email protected]>,
|
|
|
- "David S. Miller" <[email protected]>,
|
|
|
- Eric Dumazet <[email protected]>,
|
|
|
- Florian Fainelli <[email protected]>,
|
|
|
- Ioana Ciornei <[email protected]>,
|
|
|
- Jakub Kicinski <[email protected]>,
|
|
|
- Jose Abreu <[email protected]>,
|
|
|
- [email protected],
|
|
|
- [email protected],
|
|
|
- Marcin Wojtas <[email protected]>,
|
|
|
- Matthias Brugger <[email protected]>,
|
|
|
- [email protected], Paolo Abeni <[email protected]>
|
|
|
-Subject: [PATCH RFC net-next 00/16] net: add negotiation of in-band capabilities
|
|
|
-Date: Tue, 26 Nov 2024 09:23:48 +0000 [thread overview]
|
|
|
-Message-ID: <[email protected]> (raw)
|
|
|
-
|
|
|
-Hi,
|
|
|
-
|
|
|
-Yes, this is one patch over the limit of 15 for netdev - but I think it's
|
|
|
-important to include the last patch to head off review comments like "why
|
|
|
-don't you remove phylink_phy_no_inband() in this series?"
|
|
|
-
|
|
|
-Phylink's handling of in-band has been deficient for a long time, and
|
|
|
-people keep hitting problems with it. Notably, situations with the way-
|
|
|
-to-late standardized 2500Base-X and whether that should or should not
|
|
|
-have in-band enabled. We have also been carrying a hack in the form of
|
|
|
-phylink_phy_no_inband() for a PHY that has been used on a SFP module,
|
|
|
-but has no in-band capabilities, not even for SGMII.
|
|
|
-
|
|
|
-When phylink is trying to operate in in-band mode, this series will look
|
|
|
-at the capabilities of the MAC-side PCS and PHY, and work out whether
|
|
|
-in-band can or should be used, programming the PHY as appropriate. This
|
|
|
-includes in-band bypass mode at the PHY.
|
|
|
-
|
|
|
-We don't... yet... support that on the MAC side PCS, because that
|
|
|
-requires yet more complexity.
|
|
|
-
|
|
|
-Patch 1 passes struct phylink and struct phylink_pcs into
|
|
|
-phylink_pcs_neg_mode() so we can look at more state in this function in
|
|
|
-a future patch.
|
|
|
-
|
|
|
-Patch 2 splits "cur_link_an_mode" (the MLO_AN_* mode) into two separate
|
|
|
-purposes - a requested and an active mode. The active mode is the one
|
|
|
-we will be using for the MAC, which becomes dependent on the result of
|
|
|
-in-band negotiation.
|
|
|
-
|
|
|
-Patch 3 adds debug to phylink_major_config() so we can see what is going
|
|
|
-on with the requested and active AN modes.
|
|
|
-
|
|
|
-Patch 4 adds to phylib a method to get the in-band capabilities of the
|
|
|
-PHY from phylib. Patches 5 and 6 add implementations for BCM84881 and
|
|
|
-some Marvell PHYs found on SFPs.
|
|
|
-
|
|
|
-Patch 7 adds to phylib a method to configure the PHY in-band signalling,
|
|
|
-and patch 8 implements it for those Marvell PHYs that support the method
|
|
|
-in patch 4.
|
|
|
-
|
|
|
-Patch 9 does the same as patch 4 but for the MAC-side PCS, with patches
|
|
|
-10 through 14 adding support to several PCS.
|
|
|
-
|
|
|
-Patch 15 adds the code to phylink_pcs_neg_mode() which looks at the
|
|
|
-capabilities, and works out whether to use in-band or out-band mode for
|
|
|
-driving the link between the MAC PCS and PHY.
|
|
|
-
|
|
|
-Patch 16 removes the phylink_phy_no_inband() hack now that we are
|
|
|
-publishing the in-band capabilities from the BCM84881 PHY driver.
|
|
|
-
|
|
|
- drivers/net/ethernet/marvell/mvneta.c | 27 +-
|
|
|
- drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 25 +-
|
|
|
- drivers/net/pcs/pcs-lynx.c | 22 ++
|
|
|
- drivers/net/pcs/pcs-mtk-lynxi.c | 16 ++
|
|
|
- drivers/net/pcs/pcs-xpcs.c | 28 ++
|
|
|
- drivers/net/phy/bcm84881.c | 10 +
|
|
|
- drivers/net/phy/marvell.c | 48 ++++
|
|
|
- drivers/net/phy/phy.c | 52 ++++
|
|
|
- drivers/net/phy/phylink.c | 352 +++++++++++++++++++-----
|
|
|
- include/linux/phy.h | 34 +++
|
|
|
- include/linux/phylink.h | 17 ++
|
|
|
- 11 files changed, 539 insertions(+), 92 deletions(-)
|
|
|
-
|
|
|
---- a/drivers/net/phy/phylink.c
|
|
|
-+++ b/drivers/net/phy/phylink.c
|
|
|
-@@ -56,7 +56,8 @@ 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);
|
|
|
-
|
|
|
-@@ -74,6 +75,7 @@ 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;
|
|
|
-@@ -175,6 +177,24 @@ static const char *phylink_an_mode_str(u
|
|
|
- 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) {
|
|
|
-@@ -988,6 +1008,15 @@ static void phylink_resolve_an_pause(str
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-+static unsigned int phylink_pcs_inband_caps(struct phylink_pcs *pcs,
|
|
|
-+ phy_interface_t interface)
|
|
|
-+{
|
|
|
-+ if (pcs && pcs->ops->pcs_inband_caps)
|
|
|
-+ return pcs->ops->pcs_inband_caps(pcs, interface);
|
|
|
-+
|
|
|
-+ return 0;
|
|
|
-+}
|
|
|
-+
|
|
|
- static void phylink_pcs_pre_config(struct phylink_pcs *pcs,
|
|
|
- phy_interface_t interface)
|
|
|
- {
|
|
|
-@@ -1041,6 +1070,24 @@ static void phylink_pcs_link_up(struct p
|
|
|
- pcs->ops->pcs_link_up(pcs, neg_mode, interface, speed, duplex);
|
|
|
- }
|
|
|
-
|
|
|
-+/* 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 (pl->cfg_link_an_mode == MLO_AN_INBAND)
|
|
|
-@@ -1082,13 +1129,13 @@ static void phylink_mac_config(struct ph
|
|
|
-
|
|
|
- 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)
|
|
|
-@@ -1096,13 +1143,14 @@ static void phylink_pcs_an_restart(struc
|
|
|
- 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);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * phylink_pcs_neg_mode() - helper to determine PCS inband mode
|
|
|
-- * @mode: one of %MLO_AN_FIXED, %MLO_AN_PHY, %MLO_AN_INBAND.
|
|
|
-+ * @pl: a pointer to a &struct phylink returned from phylink_create()
|
|
|
-+ * @pcs: a pointer to &struct phylink_pcs
|
|
|
- * @interface: interface mode to be used
|
|
|
- * @advertising: adertisement ethtool link mode mask
|
|
|
- *
|
|
|
-@@ -1119,11 +1167,21 @@ static void phylink_pcs_an_restart(struc
|
|
|
- * 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(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 neg_mode;
|
|
|
-+ 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:
|
|
|
-@@ -1136,10 +1194,7 @@ static unsigned int phylink_pcs_neg_mode
|
|
|
- * inband communication. Note: there exist PHYs that run
|
|
|
- * with SGMII but do not send the inband data.
|
|
|
- */
|
|
|
-- if (!phylink_autoneg_inband(mode))
|
|
|
-- neg_mode = PHYLINK_PCS_NEG_OUTBAND;
|
|
|
-- else
|
|
|
-- neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED;
|
|
|
-+ type = INBAND_CISCO_SGMII;
|
|
|
- break;
|
|
|
-
|
|
|
- case PHY_INTERFACE_MODE_1000BASEX:
|
|
|
-@@ -1150,21 +1205,143 @@ static unsigned int phylink_pcs_neg_mode
|
|
|
- * as well, but drivers may not support this, so may
|
|
|
- * need to override this.
|
|
|
- */
|
|
|
-- if (!phylink_autoneg_inband(mode))
|
|
|
-+ type = INBAND_BASEX;
|
|
|
-+ break;
|
|
|
-+
|
|
|
-+ default:
|
|
|
-+ pl->pcs_neg_mode = PHYLINK_PCS_NEG_NONE;
|
|
|
-+ pl->act_link_an_mode = mode;
|
|
|
-+ return;
|
|
|
-+ }
|
|
|
-+
|
|
|
-+ if (pcs)
|
|
|
-+ pcs_ib_caps = phylink_pcs_inband_caps(pcs, interface);
|
|
|
-+
|
|
|
-+ if (pl->phydev)
|
|
|
-+ 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_ib_only || phy_ib_only)
|
|
|
-+ phylink_warn(pl,
|
|
|
-+ "firmware wants %s mode, but %s%s%s requires inband\n",
|
|
|
-+ phylink_an_mode_str(mode),
|
|
|
-+ 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.
|
|
|
-+ */
|
|
|
-+ 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_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;
|
|
|
-+
|
|
|
- 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 {
|
|
|
-+ /* 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;
|
|
|
-- break;
|
|
|
--
|
|
|
-- default:
|
|
|
-- neg_mode = PHYLINK_PCS_NEG_NONE;
|
|
|
-- break;
|
|
|
- }
|
|
|
-
|
|
|
-- 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,
|
|
|
-@@ -1176,11 +1353,9 @@ static void phylink_major_config(struct
|
|
|
- unsigned int neg_mode;
|
|
|
- int err;
|
|
|
-
|
|
|
-- phylink_dbg(pl, "major config %s\n", phy_modes(state->interface));
|
|
|
--
|
|
|
-- pl->pcs_neg_mode = phylink_pcs_neg_mode(pl->cur_link_an_mode,
|
|
|
-- state->interface,
|
|
|
-- state->advertising);
|
|
|
-+ 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) {
|
|
|
- pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface);
|
|
|
-@@ -1194,10 +1369,17 @@ static void phylink_major_config(struct
|
|
|
- pcs_changed = pcs && pl->pcs != pcs;
|
|
|
- }
|
|
|
-
|
|
|
-+ 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",
|
|
|
-@@ -1231,7 +1413,7 @@ static void phylink_major_config(struct
|
|
|
- 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;
|
|
|
-
|
|
|
-@@ -1247,13 +1429,20 @@ static void phylink_major_config(struct
|
|
|
- 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)
|
|
|
-@@ -1278,17 +1467,16 @@ static int phylink_change_inband_advert(
|
|
|
- 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->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;
|
|
|
-
|
|
|
-@@ -1353,7 +1541,7 @@ static void phylink_mac_initial_config(s
|
|
|
- {
|
|
|
- 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;
|
|
|
-@@ -1427,14 +1615,14 @@ static void phylink_link_up(struct phyli
|
|
|
-
|
|
|
- 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);
|
|
|
-
|
|
|
-@@ -1454,7 +1642,7 @@ static void phylink_link_down(struct phy
|
|
|
-
|
|
|
- if (ndev)
|
|
|
- netif_carrier_off(ndev);
|
|
|
-- 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");
|
|
|
- }
|
|
|
-@@ -1481,7 +1669,7 @@ static void phylink_resolve(struct work_
|
|
|
- link_state.link = false;
|
|
|
- retrigger = true;
|
|
|
- } else {
|
|
|
-- switch (pl->cur_link_an_mode) {
|
|
|
-+ switch (pl->act_link_an_mode) {
|
|
|
- case MLO_AN_PHY:
|
|
|
- link_state = pl->phy_state;
|
|
|
- phylink_apply_manual_flow(pl, &link_state);
|
|
|
-@@ -1671,7 +1859,7 @@ int phylink_set_fixed_link(struct phylin
|
|
|
- pl->link_config.an_complete = 1;
|
|
|
-
|
|
|
- pl->cfg_link_an_mode = MLO_AN_FIXED;
|
|
|
-- pl->cur_link_an_mode = pl->cfg_link_an_mode;
|
|
|
-+ pl->req_link_an_mode = pl->cfg_link_an_mode;
|
|
|
-
|
|
|
- return 0;
|
|
|
- }
|
|
|
-@@ -1766,7 +1954,7 @@ struct phylink *phylink_create(struct ph
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-- 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) {
|
|
|
-@@ -2242,7 +2430,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 */
|
|
|
-@@ -2501,7 +2689,7 @@ int phylink_ethtool_ksettings_get(struct
|
|
|
-
|
|
|
- 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
|
|
|
-@@ -2532,6 +2720,26 @@ int phylink_ethtool_ksettings_get(struct
|
|
|
- }
|
|
|
- EXPORT_SYMBOL_GPL(phylink_ethtool_ksettings_get);
|
|
|
-
|
|
|
-+static bool phylink_validate_pcs_inband_autoneg(struct phylink *pl,
|
|
|
-+ phy_interface_t interface,
|
|
|
-+ unsigned long *adv)
|
|
|
-+{
|
|
|
-+ unsigned int inband = phylink_inband_caps(pl, interface);
|
|
|
-+ unsigned int mask;
|
|
|
-+
|
|
|
-+ /* If the PCS doesn't implement inband support, be permissive. */
|
|
|
-+ if (!inband)
|
|
|
-+ return true;
|
|
|
-+
|
|
|
-+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, adv))
|
|
|
-+ mask = LINK_INBAND_ENABLE;
|
|
|
-+ else
|
|
|
-+ mask = LINK_INBAND_DISABLE;
|
|
|
-+
|
|
|
-+ /* Check whether the PCS implements the required mode */
|
|
|
-+ return !!(inband & mask);
|
|
|
-+}
|
|
|
-+
|
|
|
- /**
|
|
|
- * phylink_ethtool_ksettings_set() - set the link settings
|
|
|
- * @pl: a pointer to a &struct phylink returned from phylink_create()
|
|
|
-@@ -2593,7 +2801,7 @@ int phylink_ethtool_ksettings_set(struct
|
|
|
- /* If we have a fixed link, refuse to change link parameters.
|
|
|
- * If the link parameters match, accept them but do nothing.
|
|
|
- */
|
|
|
-- if (pl->cur_link_an_mode == MLO_AN_FIXED) {
|
|
|
-+ if (pl->req_link_an_mode == MLO_AN_FIXED) {
|
|
|
- if (s->speed != pl->link_config.speed ||
|
|
|
- s->duplex != pl->link_config.duplex)
|
|
|
- return -EINVAL;
|
|
|
-@@ -2609,7 +2817,7 @@ int phylink_ethtool_ksettings_set(struct
|
|
|
- * is our default case) but do not allow the advertisement to
|
|
|
- * be changed. If the advertisement matches, simply return.
|
|
|
- */
|
|
|
-- if (pl->cur_link_an_mode == MLO_AN_FIXED) {
|
|
|
-+ if (pl->req_link_an_mode == MLO_AN_FIXED) {
|
|
|
- if (!linkmode_equal(config.advertising,
|
|
|
- pl->link_config.advertising))
|
|
|
- return -EINVAL;
|
|
|
-@@ -2649,7 +2857,7 @@ int phylink_ethtool_ksettings_set(struct
|
|
|
- linkmode_copy(support, pl->supported);
|
|
|
- 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;
|
|
|
-@@ -2667,6 +2875,13 @@ int phylink_ethtool_ksettings_set(struct
|
|
|
- phylink_is_empty_linkmode(config.advertising))
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
-+ /* 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_inband_autoneg(pl, config.interface,
|
|
|
-+ config.advertising))
|
|
|
-+ return -EINVAL;
|
|
|
-+
|
|
|
- mutex_lock(&pl->state_mutex);
|
|
|
- pl->link_config.speed = config.speed;
|
|
|
- pl->link_config.duplex = config.duplex;
|
|
|
-@@ -2749,7 +2964,7 @@ int phylink_ethtool_set_pauseparam(struc
|
|
|
-
|
|
|
- ASSERT_RTNL();
|
|
|
-
|
|
|
-- if (pl->cur_link_an_mode == MLO_AN_FIXED)
|
|
|
-+ if (pl->req_link_an_mode == MLO_AN_FIXED)
|
|
|
- return -EOPNOTSUPP;
|
|
|
-
|
|
|
- if (!phylink_test(pl->supported, Pause) &&
|
|
|
-@@ -3013,7 +3228,7 @@ static int phylink_mii_read(struct phyli
|
|
|
- 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);
|
|
|
-@@ -3038,7 +3253,7 @@ static int phylink_mii_read(struct phyli
|
|
|
- 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;
|
|
|
-
|
|
|
-@@ -3208,10 +3423,11 @@ static phy_interface_t phylink_choose_sf
|
|
|
- return interface;
|
|
|
- }
|
|
|
-
|
|
|
--static void phylink_sfp_set_config(struct phylink *pl, u8 mode,
|
|
|
-+static void phylink_sfp_set_config(struct phylink *pl,
|
|
|
- unsigned long *supported,
|
|
|
- struct phylink_link_state *state)
|
|
|
- {
|
|
|
-+ u8 mode = MLO_AN_INBAND;
|
|
|
- bool changed = false;
|
|
|
-
|
|
|
- phylink_dbg(pl, "requesting link mode %s/%s with support %*pb\n",
|
|
|
-@@ -3228,9 +3444,9 @@ static void phylink_sfp_set_config(struc
|
|
|
- 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;
|
|
|
-@@ -3245,8 +3461,7 @@ static void phylink_sfp_set_config(struc
|
|
|
- phylink_mac_initial_config(pl, false);
|
|
|
- }
|
|
|
-
|
|
|
--static int phylink_sfp_config_phy(struct phylink *pl, u8 mode,
|
|
|
-- struct phy_device *phy)
|
|
|
-+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);
|
|
|
-@@ -3285,8 +3500,7 @@ static int phylink_sfp_config_phy(struct
|
|
|
- ret = phylink_validate(pl, support1, &config);
|
|
|
- if (ret) {
|
|
|
- phylink_err(pl,
|
|
|
-- "validation of %s/%s with support %*pb failed: %pe\n",
|
|
|
-- phylink_an_mode_str(mode),
|
|
|
-+ "validation of %s with support %*pb failed: %pe\n",
|
|
|
- phy_modes(config.interface),
|
|
|
- __ETHTOOL_LINK_MODE_MASK_NBITS, support,
|
|
|
- ERR_PTR(ret));
|
|
|
-@@ -3295,7 +3509,7 @@ static int phylink_sfp_config_phy(struct
|
|
|
-
|
|
|
- pl->link_port = pl->sfp_port;
|
|
|
-
|
|
|
-- phylink_sfp_set_config(pl, mode, support, &config);
|
|
|
-+ phylink_sfp_set_config(pl, support, &config);
|
|
|
-
|
|
|
- return 0;
|
|
|
- }
|
|
|
-@@ -3351,6 +3565,12 @@ static int phylink_sfp_config_optical(st
|
|
|
- phylink_dbg(pl, "optical SFP: chosen %s interface\n",
|
|
|
- phy_modes(interface));
|
|
|
-
|
|
|
-+ if (!phylink_validate_pcs_inband_autoneg(pl, interface,
|
|
|
-+ config.advertising)) {
|
|
|
-+ phylink_err(pl, "autoneg setting not compatible with PCS");
|
|
|
-+ return -EINVAL;
|
|
|
-+ }
|
|
|
-+
|
|
|
- config.interface = interface;
|
|
|
-
|
|
|
- /* Ignore errors if we're expecting a PHY to attach later */
|
|
|
-@@ -3364,7 +3584,7 @@ static int phylink_sfp_config_optical(st
|
|
|
-
|
|
|
- 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);
|
|
|
-
|
|
|
- return 0;
|
|
|
- }
|
|
|
-@@ -3435,20 +3655,10 @@ static void phylink_sfp_link_up(void *up
|
|
|
- phylink_enable_and_run_resolve(pl, PHYLINK_DISABLE_LINK);
|
|
|
- }
|
|
|
-
|
|
|
--/* 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_id_compare(phy->c45_ids.device_ids[1],
|
|
|
-- 0xae025150, 0xfffffff0);
|
|
|
--}
|
|
|
--
|
|
|
- static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy)
|
|
|
- {
|
|
|
- struct phylink *pl = upstream;
|
|
|
- phy_interface_t interface;
|
|
|
-- u8 mode;
|
|
|
- int ret;
|
|
|
-
|
|
|
- /*
|
|
|
-@@ -3460,17 +3670,12 @@ static int phylink_sfp_connect_phy(void
|
|
|
- */
|
|
|
- phy_support_asym_pause(phy);
|
|
|
-
|
|
|
-- if (phylink_phy_no_inband(phy))
|
|
|
-- mode = MLO_AN_PHY;
|
|
|
-- else
|
|
|
-- mode = MLO_AN_INBAND;
|
|
|
--
|
|
|
- /* Set the PHY's host supported interfaces */
|
|
|
- phy_interface_and(phy->host_interfaces, phylink_sfp_interfaces,
|
|
|
- pl->config->supported_interfaces);
|
|
|
-
|
|
|
- /* Do the initial configuration */
|
|
|
-- ret = phylink_sfp_config_phy(pl, mode, phy);
|
|
|
-+ ret = phylink_sfp_config_phy(pl, phy);
|
|
|
- if (ret < 0)
|
|
|
- return ret;
|
|
|
-
|
|
|
---- a/drivers/net/phy/phy.c
|
|
|
-+++ b/drivers/net/phy/phy.c
|
|
|
-@@ -1049,6 +1049,58 @@ static int phy_check_link_status(struct
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
-+ * phy_inband_caps - query which in-band signalling modes are supported
|
|
|
-+ * @phydev: a pointer to a &struct phy_device
|
|
|
-+ * @interface: the interface mode for the PHY
|
|
|
-+ *
|
|
|
-+ * Returns zero if it is unknown what in-band signalling is supported by the
|
|
|
-+ * PHY (e.g. because the PHY driver doesn't implement the method.) Otherwise,
|
|
|
-+ * returns a bit mask of the LINK_INBAND_* values from
|
|
|
-+ * &enum link_inband_signalling to describe which inband modes are supported
|
|
|
-+ * by the PHY for this interface mode.
|
|
|
-+ */
|
|
|
-+unsigned int phy_inband_caps(struct phy_device *phydev,
|
|
|
-+ phy_interface_t interface)
|
|
|
-+{
|
|
|
-+ if (phydev->drv && phydev->drv->inband_caps)
|
|
|
-+ return phydev->drv->inband_caps(phydev, interface);
|
|
|
-+
|
|
|
-+ return 0;
|
|
|
-+}
|
|
|
-+EXPORT_SYMBOL_GPL(phy_inband_caps);
|
|
|
-+
|
|
|
-+/**
|
|
|
-+ * phy_config_inband - configure the desired PHY in-band mode
|
|
|
-+ * @phydev: the phy_device struct
|
|
|
-+ * @modes: in-band modes to configure
|
|
|
-+ *
|
|
|
-+ * Description: disables, enables or enables-with-bypass in-band signalling
|
|
|
-+ * between the PHY and host system.
|
|
|
-+ *
|
|
|
-+ * Returns: zero on success, or negative errno value.
|
|
|
-+ */
|
|
|
-+int phy_config_inband(struct phy_device *phydev, unsigned int modes)
|
|
|
-+{
|
|
|
-+ int err;
|
|
|
-+
|
|
|
-+ if (!!(modes & LINK_INBAND_DISABLE) +
|
|
|
-+ !!(modes & LINK_INBAND_ENABLE) +
|
|
|
-+ !!(modes & LINK_INBAND_BYPASS) != 1)
|
|
|
-+ return -EINVAL;
|
|
|
-+
|
|
|
-+ mutex_lock(&phydev->lock);
|
|
|
-+ if (!phydev->drv)
|
|
|
-+ err = -EIO;
|
|
|
-+ else if (!phydev->drv->config_inband)
|
|
|
-+ err = -EOPNOTSUPP;
|
|
|
-+ else
|
|
|
-+ err = phydev->drv->config_inband(phydev, modes);
|
|
|
-+ mutex_unlock(&phydev->lock);
|
|
|
-+
|
|
|
-+ return err;
|
|
|
-+}
|
|
|
-+
|
|
|
-+/**
|
|
|
- * _phy_start_aneg - start auto-negotiation for this PHY device
|
|
|
- * @phydev: the phy_device struct
|
|
|
- *
|
|
|
---- a/include/linux/phy.h
|
|
|
-+++ b/include/linux/phy.h
|
|
|
-@@ -816,6 +816,24 @@ struct phy_tdr_config {
|
|
|
- #define PHY_PAIR_ALL -1
|
|
|
-
|
|
|
- /**
|
|
|
-+ * enum link_inband_signalling - in-band signalling modes that are supported
|
|
|
-+ *
|
|
|
-+ * @LINK_INBAND_DISABLE: in-band signalling can be disabled
|
|
|
-+ * @LINK_INBAND_ENABLE: in-band signalling can be enabled without bypass
|
|
|
-+ * @LINK_INBAND_BYPASS: in-band signalling can be enabled with bypass
|
|
|
-+ *
|
|
|
-+ * The possible and required bits can only be used if the valid bit is set.
|
|
|
-+ * If possible is clear, that means inband signalling can not be used.
|
|
|
-+ * Required is only valid when possible is set, and means that inband
|
|
|
-+ * signalling must be used.
|
|
|
-+ */
|
|
|
-+enum link_inband_signalling {
|
|
|
-+ LINK_INBAND_DISABLE = BIT(0),
|
|
|
-+ LINK_INBAND_ENABLE = BIT(1),
|
|
|
-+ LINK_INBAND_BYPASS = BIT(2),
|
|
|
-+};
|
|
|
-+
|
|
|
-+/**
|
|
|
- * struct phy_plca_cfg - Configuration of the PLCA (Physical Layer Collision
|
|
|
- * Avoidance) Reconciliation Sublayer.
|
|
|
- *
|
|
|
-@@ -955,6 +973,19 @@ struct phy_driver {
|
|
|
- int (*get_features)(struct phy_device *phydev);
|
|
|
-
|
|
|
- /**
|
|
|
-+ * @inband_caps: query whether in-band is supported for the given PHY
|
|
|
-+ * interface mode. Returns a bitmask of bits defined by enum
|
|
|
-+ * link_inband_signalling.
|
|
|
-+ */
|
|
|
-+ unsigned int (*inband_caps)(struct phy_device *phydev,
|
|
|
-+ phy_interface_t interface);
|
|
|
-+
|
|
|
-+ /**
|
|
|
-+ * @config_inband: configure in-band mode for the PHY
|
|
|
-+ */
|
|
|
-+ int (*config_inband)(struct phy_device *phydev, unsigned int modes);
|
|
|
-+
|
|
|
-+ /**
|
|
|
- * @get_rate_matching: Get the supported type of rate matching for a
|
|
|
- * particular phy interface. This is used by phy consumers to determine
|
|
|
- * whether to advertise lower-speed modes for that interface. It is
|
|
|
-@@ -1840,6 +1871,9 @@ int phy_config_aneg(struct phy_device *p
|
|
|
- int _phy_start_aneg(struct phy_device *phydev);
|
|
|
- int phy_start_aneg(struct phy_device *phydev);
|
|
|
- int phy_aneg_done(struct phy_device *phydev);
|
|
|
-+unsigned int phy_inband_caps(struct phy_device *phydev,
|
|
|
-+ phy_interface_t interface);
|
|
|
-+int phy_config_inband(struct phy_device *phydev, unsigned int modes);
|
|
|
- int phy_speed_down(struct phy_device *phydev, bool sync);
|
|
|
- int phy_speed_up(struct phy_device *phydev);
|
|
|
- bool phy_check_valid(int speed, int duplex, unsigned long *features);
|
|
|
---- a/drivers/net/phy/bcm84881.c
|
|
|
-+++ b/drivers/net/phy/bcm84881.c
|
|
|
-@@ -235,11 +235,21 @@ static int bcm84881_read_status(struct p
|
|
|
- return genphy_c45_read_mdix(phydev);
|
|
|
- }
|
|
|
-
|
|
|
-+/* The Broadcom BCM84881 in the Methode DM7052 is unable to provide a SGMII
|
|
|
-+ * or 802.3z control word, so inband will not work.
|
|
|
-+ */
|
|
|
-+static unsigned int bcm84881_inband_caps(struct phy_device *phydev,
|
|
|
-+ phy_interface_t interface)
|
|
|
-+{
|
|
|
-+ return LINK_INBAND_DISABLE;
|
|
|
-+}
|
|
|
-+
|
|
|
- static struct phy_driver bcm84881_drivers[] = {
|
|
|
- {
|
|
|
- .phy_id = 0xae025150,
|
|
|
- .phy_id_mask = 0xfffffff0,
|
|
|
- .name = "Broadcom BCM84881",
|
|
|
-+ .inband_caps = bcm84881_inband_caps,
|
|
|
- .config_init = bcm84881_config_init,
|
|
|
- .probe = bcm84881_probe,
|
|
|
- .get_features = bcm84881_get_features,
|
|
|
---- a/drivers/net/phy/marvell.c
|
|
|
-+++ b/drivers/net/phy/marvell.c
|
|
|
-@@ -716,6 +716,48 @@ static int marvell_config_aneg_fiber(str
|
|
|
- return genphy_check_and_restart_aneg(phydev, changed);
|
|
|
- }
|
|
|
-
|
|
|
-+static unsigned int m88e1111_inband_caps(struct phy_device *phydev,
|
|
|
-+ phy_interface_t interface)
|
|
|
-+{
|
|
|
-+ /* In 1000base-X and SGMII modes, the inband mode can be changed
|
|
|
-+ * through the Fibre page BMCR ANENABLE bit.
|
|
|
-+ */
|
|
|
-+ if (interface == PHY_INTERFACE_MODE_1000BASEX ||
|
|
|
-+ interface == PHY_INTERFACE_MODE_SGMII)
|
|
|
-+ return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE |
|
|
|
-+ LINK_INBAND_BYPASS;
|
|
|
-+
|
|
|
-+ return 0;
|
|
|
-+}
|
|
|
-+
|
|
|
-+static int m88e1111_config_inband(struct phy_device *phydev, unsigned int modes)
|
|
|
-+{
|
|
|
-+ u16 extsr, bmcr;
|
|
|
-+ int err;
|
|
|
-+
|
|
|
-+ if (phydev->interface != PHY_INTERFACE_MODE_1000BASEX &&
|
|
|
-+ phydev->interface != PHY_INTERFACE_MODE_SGMII)
|
|
|
-+ return -EINVAL;
|
|
|
-+
|
|
|
-+ if (modes == LINK_INBAND_BYPASS)
|
|
|
-+ extsr = MII_M1111_HWCFG_SERIAL_AN_BYPASS;
|
|
|
-+ else
|
|
|
-+ extsr = 0;
|
|
|
-+
|
|
|
-+ if (modes == LINK_INBAND_DISABLE)
|
|
|
-+ bmcr = 0;
|
|
|
-+ else
|
|
|
-+ bmcr = BMCR_ANENABLE;
|
|
|
-+
|
|
|
-+ err = phy_modify(phydev, MII_M1111_PHY_EXT_SR,
|
|
|
-+ MII_M1111_HWCFG_SERIAL_AN_BYPASS, extsr);
|
|
|
-+ if (err < 0)
|
|
|
-+ return extsr;
|
|
|
-+
|
|
|
-+ return phy_modify_paged(phydev, MII_MARVELL_FIBER_PAGE, MII_BMCR,
|
|
|
-+ BMCR_ANENABLE, bmcr);
|
|
|
-+}
|
|
|
-+
|
|
|
- static int m88e1111_config_aneg(struct phy_device *phydev)
|
|
|
- {
|
|
|
- int extsr = phy_read(phydev, MII_M1111_PHY_EXT_SR);
|
|
|
-@@ -3667,6 +3709,8 @@ static struct phy_driver marvell_drivers
|
|
|
- .name = "Marvell 88E1112",
|
|
|
- /* PHY_GBIT_FEATURES */
|
|
|
- .probe = marvell_probe,
|
|
|
-+ .inband_caps = m88e1111_inband_caps,
|
|
|
-+ .config_inband = m88e1111_config_inband,
|
|
|
- .config_init = m88e1112_config_init,
|
|
|
- .config_aneg = marvell_config_aneg,
|
|
|
- .config_intr = marvell_config_intr,
|
|
|
-@@ -3688,6 +3732,8 @@ static struct phy_driver marvell_drivers
|
|
|
- /* PHY_GBIT_FEATURES */
|
|
|
- .flags = PHY_POLL_CABLE_TEST,
|
|
|
- .probe = marvell_probe,
|
|
|
-+ .inband_caps = m88e1111_inband_caps,
|
|
|
-+ .config_inband = m88e1111_config_inband,
|
|
|
- .config_init = m88e1111gbe_config_init,
|
|
|
- .config_aneg = m88e1111_config_aneg,
|
|
|
- .read_status = marvell_read_status,
|
|
|
-@@ -3711,6 +3757,8 @@ static struct phy_driver marvell_drivers
|
|
|
- .name = "Marvell 88E1111 (Finisar)",
|
|
|
- /* PHY_GBIT_FEATURES */
|
|
|
- .probe = marvell_probe,
|
|
|
-+ .inband_caps = m88e1111_inband_caps,
|
|
|
-+ .config_inband = m88e1111_config_inband,
|
|
|
- .config_init = m88e1111gbe_config_init,
|
|
|
- .config_aneg = m88e1111_config_aneg,
|
|
|
- .read_status = marvell_read_status,
|
|
|
---- a/include/linux/phylink.h
|
|
|
-+++ b/include/linux/phylink.h
|
|
|
-@@ -419,6 +419,7 @@ struct phylink_pcs {
|
|
|
- /**
|
|
|
- * struct phylink_pcs_ops - MAC PCS operations structure.
|
|
|
- * @pcs_validate: validate the link configuration.
|
|
|
-+ * @pcs_inband_caps: query inband support for interface mode.
|
|
|
- * @pcs_enable: enable the PCS.
|
|
|
- * @pcs_disable: disable the PCS.
|
|
|
- * @pcs_pre_config: pre-mac_config method (for errata)
|
|
|
-@@ -434,6 +435,8 @@ struct phylink_pcs {
|
|
|
- struct phylink_pcs_ops {
|
|
|
- int (*pcs_validate)(struct phylink_pcs *pcs, unsigned long *supported,
|
|
|
- const struct phylink_link_state *state);
|
|
|
-+ unsigned int (*pcs_inband_caps)(struct phylink_pcs *pcs,
|
|
|
-+ phy_interface_t interface);
|
|
|
- int (*pcs_enable)(struct phylink_pcs *pcs);
|
|
|
- void (*pcs_disable)(struct phylink_pcs *pcs);
|
|
|
- void (*pcs_pre_config)(struct phylink_pcs *pcs,
|
|
|
-@@ -471,6 +474,20 @@ int pcs_validate(struct phylink_pcs *pcs
|
|
|
- const struct phylink_link_state *state);
|
|
|
-
|
|
|
- /**
|
|
|
-+ * pcs_inband_caps - query PCS in-band capabilities for interface mode.
|
|
|
-+ * @pcs: a pointer to a &struct phylink_pcs.
|
|
|
-+ * @interface: interface mode to be queried
|
|
|
-+ *
|
|
|
-+ * Returns zero if it is unknown what in-band signalling is supported by the
|
|
|
-+ * PHY (e.g. because the PHY driver doesn't implement the method.) Otherwise,
|
|
|
-+ * returns a bit mask of the LINK_INBAND_* values from
|
|
|
-+ * &enum link_inband_signalling to describe which inband modes are supported
|
|
|
-+ * for this interface mode.
|
|
|
-+ */
|
|
|
-+unsigned int pcs_inband_caps(struct phylink_pcs *pcs,
|
|
|
-+ phy_interface_t interface);
|
|
|
-+
|
|
|
-+/**
|
|
|
- * pcs_enable() - enable the PCS.
|
|
|
- * @pcs: a pointer to a &struct phylink_pcs.
|
|
|
- */
|
|
|
---- a/drivers/net/ethernet/marvell/mvneta.c
|
|
|
-+++ b/drivers/net/ethernet/marvell/mvneta.c
|
|
|
-@@ -3960,20 +3960,27 @@ static struct mvneta_port *mvneta_pcs_to
|
|
|
- return container_of(pcs, struct mvneta_port, phylink_pcs);
|
|
|
- }
|
|
|
-
|
|
|
--static int mvneta_pcs_validate(struct phylink_pcs *pcs,
|
|
|
-- unsigned long *supported,
|
|
|
-- const struct phylink_link_state *state)
|
|
|
-+static unsigned int mvneta_pcs_inband_caps(struct phylink_pcs *pcs,
|
|
|
-+ phy_interface_t interface)
|
|
|
- {
|
|
|
-- /* We only support QSGMII, SGMII, 802.3z and RGMII modes.
|
|
|
-- * When in 802.3z mode, we must have AN enabled:
|
|
|
-+ /* When operating in an 802.3z mode, we must have AN enabled:
|
|
|
- * "Bit 2 Field InBandAnEn In-band Auto-Negotiation enable. ...
|
|
|
- * When <PortType> = 1 (1000BASE-X) this field must be set to 1."
|
|
|
-+ * Therefore, inband is "required".
|
|
|
- */
|
|
|
-- if (phy_interface_mode_is_8023z(state->interface) &&
|
|
|
-- !phylink_test(state->advertising, Autoneg))
|
|
|
-- return -EINVAL;
|
|
|
-+ if (phy_interface_mode_is_8023z(interface))
|
|
|
-+ return LINK_INBAND_ENABLE;
|
|
|
-
|
|
|
-- return 0;
|
|
|
-+ /* QSGMII, SGMII and RGMII can be configured to use inband
|
|
|
-+ * signalling of the AN result. Indicate these as "possible".
|
|
|
-+ */
|
|
|
-+ if (interface == PHY_INTERFACE_MODE_SGMII ||
|
|
|
-+ interface == PHY_INTERFACE_MODE_QSGMII ||
|
|
|
-+ phy_interface_mode_is_rgmii(interface))
|
|
|
-+ return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
|
|
|
-+
|
|
|
-+ /* For any other modes, indicate that inband is not supported. */
|
|
|
-+ return LINK_INBAND_DISABLE;
|
|
|
- }
|
|
|
-
|
|
|
- static void mvneta_pcs_get_state(struct phylink_pcs *pcs,
|
|
|
-@@ -4071,7 +4078,7 @@ static void mvneta_pcs_an_restart(struct
|
|
|
- }
|
|
|
-
|
|
|
- static const struct phylink_pcs_ops mvneta_phylink_pcs_ops = {
|
|
|
-- .pcs_validate = mvneta_pcs_validate,
|
|
|
-+ .pcs_inband_caps = mvneta_pcs_inband_caps,
|
|
|
- .pcs_get_state = mvneta_pcs_get_state,
|
|
|
- .pcs_config = mvneta_pcs_config,
|
|
|
- .pcs_an_restart = mvneta_pcs_an_restart,
|
|
|
---- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
|
|
|
-+++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
|
|
|
-@@ -6237,19 +6237,26 @@ static const struct phylink_pcs_ops mvpp
|
|
|
- .pcs_config = mvpp2_xlg_pcs_config,
|
|
|
- };
|
|
|
-
|
|
|
--static int mvpp2_gmac_pcs_validate(struct phylink_pcs *pcs,
|
|
|
-- unsigned long *supported,
|
|
|
-- const struct phylink_link_state *state)
|
|
|
-+static unsigned int mvpp2_gmac_pcs_inband_caps(struct phylink_pcs *pcs,
|
|
|
-+ phy_interface_t interface)
|
|
|
- {
|
|
|
-- /* When in 802.3z mode, we must have AN enabled:
|
|
|
-+ /* When operating in an 802.3z mode, we must have AN enabled:
|
|
|
- * Bit 2 Field InBandAnEn In-band Auto-Negotiation enable. ...
|
|
|
- * When <PortType> = 1 (1000BASE-X) this field must be set to 1.
|
|
|
-+ * Therefore, inband is "required".
|
|
|
- */
|
|
|
-- if (phy_interface_mode_is_8023z(state->interface) &&
|
|
|
-- !phylink_test(state->advertising, Autoneg))
|
|
|
-- return -EINVAL;
|
|
|
-+ if (phy_interface_mode_is_8023z(interface))
|
|
|
-+ return LINK_INBAND_ENABLE;
|
|
|
-
|
|
|
-- return 0;
|
|
|
-+ /* SGMII and RGMII can be configured to use inband signalling of the
|
|
|
-+ * AN result. Indicate these as "possible".
|
|
|
-+ */
|
|
|
-+ if (interface == PHY_INTERFACE_MODE_SGMII ||
|
|
|
-+ phy_interface_mode_is_rgmii(interface))
|
|
|
-+ return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
|
|
|
-+
|
|
|
-+ /* For any other modes, indicate that inband is not supported. */
|
|
|
-+ return LINK_INBAND_DISABLE;
|
|
|
- }
|
|
|
-
|
|
|
- static void mvpp2_gmac_pcs_get_state(struct phylink_pcs *pcs,
|
|
|
-@@ -6356,7 +6363,7 @@ static void mvpp2_gmac_pcs_an_restart(st
|
|
|
- }
|
|
|
-
|
|
|
- static const struct phylink_pcs_ops mvpp2_phylink_gmac_pcs_ops = {
|
|
|
-- .pcs_validate = mvpp2_gmac_pcs_validate,
|
|
|
-+ .pcs_inband_caps = mvpp2_gmac_pcs_inband_caps,
|
|
|
- .pcs_get_state = mvpp2_gmac_pcs_get_state,
|
|
|
- .pcs_config = mvpp2_gmac_pcs_config,
|
|
|
- .pcs_an_restart = mvpp2_gmac_pcs_an_restart,
|
|
|
---- a/drivers/net/pcs/pcs-lynx.c
|
|
|
-+++ b/drivers/net/pcs/pcs-lynx.c
|
|
|
-@@ -35,6 +35,27 @@ enum sgmii_speed {
|
|
|
- #define phylink_pcs_to_lynx(pl_pcs) container_of((pl_pcs), struct lynx_pcs, pcs)
|
|
|
- #define lynx_to_phylink_pcs(lynx) (&(lynx)->pcs)
|
|
|
-
|
|
|
-+static unsigned int lynx_pcs_inband_caps(struct phylink_pcs *pcs,
|
|
|
-+ phy_interface_t interface)
|
|
|
-+{
|
|
|
-+ switch (interface) {
|
|
|
-+ case PHY_INTERFACE_MODE_1000BASEX:
|
|
|
-+ case PHY_INTERFACE_MODE_SGMII:
|
|
|
-+ case PHY_INTERFACE_MODE_QSGMII:
|
|
|
-+ return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
|
|
|
-+
|
|
|
-+ case PHY_INTERFACE_MODE_10GBASER:
|
|
|
-+ case PHY_INTERFACE_MODE_2500BASEX:
|
|
|
-+ return LINK_INBAND_DISABLE;
|
|
|
-+
|
|
|
-+ case PHY_INTERFACE_MODE_USXGMII:
|
|
|
-+ return LINK_INBAND_ENABLE;
|
|
|
-+
|
|
|
-+ default:
|
|
|
-+ return 0;
|
|
|
-+ }
|
|
|
-+}
|
|
|
-+
|
|
|
- static void lynx_pcs_get_state_usxgmii(struct mdio_device *pcs,
|
|
|
- struct phylink_link_state *state)
|
|
|
- {
|
|
|
-@@ -306,6 +327,7 @@ static void lynx_pcs_link_up(struct phyl
|
|
|
- }
|
|
|
-
|
|
|
- static const struct phylink_pcs_ops lynx_pcs_phylink_ops = {
|
|
|
-+ .pcs_inband_caps = lynx_pcs_inband_caps,
|
|
|
- .pcs_get_state = lynx_pcs_get_state,
|
|
|
- .pcs_config = lynx_pcs_config,
|
|
|
- .pcs_an_restart = lynx_pcs_an_restart,
|
|
|
---- a/drivers/net/pcs/pcs-mtk-lynxi.c
|
|
|
-+++ b/drivers/net/pcs/pcs-mtk-lynxi.c
|
|
|
-@@ -110,6 +110,21 @@ static struct mtk_pcs_lynxi *pcs_to_mtk_
|
|
|
- return container_of(pcs, struct mtk_pcs_lynxi, pcs);
|
|
|
- }
|
|
|
-
|
|
|
-+static unsigned int mtk_pcs_lynxi_inband_caps(struct phylink_pcs *pcs,
|
|
|
-+ phy_interface_t interface)
|
|
|
-+{
|
|
|
-+ switch (interface) {
|
|
|
-+ case PHY_INTERFACE_MODE_1000BASEX:
|
|
|
-+ case PHY_INTERFACE_MODE_2500BASEX:
|
|
|
-+ case PHY_INTERFACE_MODE_SGMII:
|
|
|
-+ case PHY_INTERFACE_MODE_QSGMII:
|
|
|
-+ return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
|
|
|
-+
|
|
|
-+ default:
|
|
|
-+ return 0;
|
|
|
-+ }
|
|
|
-+}
|
|
|
-+
|
|
|
- static void mtk_pcs_lynxi_get_state(struct phylink_pcs *pcs,
|
|
|
- struct phylink_link_state *state)
|
|
|
- {
|
|
|
-@@ -302,6 +317,7 @@ static void mtk_pcs_lynxi_disable(struct
|
|
|
- }
|
|
|
-
|
|
|
- static const struct phylink_pcs_ops mtk_pcs_lynxi_ops = {
|
|
|
-+ .pcs_inband_caps = mtk_pcs_lynxi_inband_caps,
|
|
|
- .pcs_get_state = mtk_pcs_lynxi_get_state,
|
|
|
- .pcs_config = mtk_pcs_lynxi_config,
|
|
|
- .pcs_an_restart = mtk_pcs_lynxi_restart_an,
|
|
|
---- a/drivers/net/pcs/pcs-xpcs.c
|
|
|
-+++ b/drivers/net/pcs/pcs-xpcs.c
|
|
|
-@@ -608,6 +608,33 @@ static int xpcs_validate(struct phylink_
|
|
|
- return 0;
|
|
|
- }
|
|
|
-
|
|
|
-+static unsigned int xpcs_inband_caps(struct phylink_pcs *pcs,
|
|
|
-+ phy_interface_t interface)
|
|
|
-+{
|
|
|
-+ struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs);
|
|
|
-+ const struct dw_xpcs_compat *compat;
|
|
|
-+
|
|
|
-+ compat = xpcs_find_compat(xpcs, interface);
|
|
|
-+ if (!compat)
|
|
|
-+ return 0;
|
|
|
-+
|
|
|
-+ switch (compat->an_mode) {
|
|
|
-+ case DW_AN_C73:
|
|
|
-+ return LINK_INBAND_ENABLE;
|
|
|
-+
|
|
|
-+ case DW_AN_C37_SGMII:
|
|
|
-+ case DW_AN_C37_1000BASEX:
|
|
|
-+ return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
|
|
|
-+
|
|
|
-+ case DW_10GBASER:
|
|
|
-+ case DW_2500BASEX:
|
|
|
-+ return LINK_INBAND_DISABLE;
|
|
|
-+
|
|
|
-+ default:
|
|
|
-+ return 0;
|
|
|
-+ }
|
|
|
-+}
|
|
|
-+
|
|
|
- void xpcs_get_interfaces(struct dw_xpcs *xpcs, unsigned long *interfaces)
|
|
|
- {
|
|
|
- int i, j;
|
|
|
-@@ -1365,6 +1392,7 @@ static const struct dw_xpcs_desc xpcs_de
|
|
|
-
|
|
|
- static const struct phylink_pcs_ops xpcs_phylink_ops = {
|
|
|
- .pcs_validate = xpcs_validate,
|
|
|
-+ .pcs_inband_caps = xpcs_inband_caps,
|
|
|
- .pcs_config = xpcs_config,
|
|
|
- .pcs_get_state = xpcs_get_state,
|
|
|
- .pcs_an_restart = xpcs_an_restart,
|