--- /dev/null
+From 1a7aa058bc92f0edae7a0d1ef1a7b05aec0c643a Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Fri, 24 Nov 2023 12:27:52 +0000
+Subject: [PATCH 1/7] net: phy: add possible interfaces
+
+Add a possible_interfaces member to struct phy_device to indicate which
+interfaces a clause 45 PHY may switch between depending on the media.
+This must be populated by the PHY driver by the time the .config_init()
+method completes according to the PHYs host-side configuration.
+
+For example, the Marvell 88x3310 PHY can switch between 10GBASE-R,
+5GBASE-R, 2500BASE-X, and SGMII on the host side depending on the media
+side speed, so all these interface modes are set in the
+possible_interfaces member.
+
+This allows phylib users (such as phylink) to know in advance which
+interface modes to expect, which allows them to appropriately restrict
+the advertised link modes according to the capabilities of other parts
+of the link.
+
+Tested-by: Luo Jie <quic_luoj@quicinc.com>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/E1r6VHk-00DDLN-I7@rmk-PC.armlinux.org.uk
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/phy_device.c | 2 ++
+ include/linux/phy.h | 3 +++
+ 2 files changed, 5 insertions(+)
+
+--- a/drivers/net/phy/phy_device.c
++++ b/drivers/net/phy/phy_device.c
+@@ -1215,6 +1215,8 @@ int phy_init_hw(struct phy_device *phyde
+ if (ret < 0)
+ return ret;
+
++ phy_interface_zero(phydev->possible_interfaces);
++
+ if (phydev->drv->config_init) {
+ ret = phydev->drv->config_init(phydev);
+ if (ret < 0)
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -600,6 +600,8 @@ struct macsec_ops;
+ * @irq_rerun: Flag indicating interrupts occurred while PHY was suspended,
+ * requiring a rerun of the interrupt handler after resume
+ * @interface: enum phy_interface_t value
++ * @possible_interfaces: bitmap if interface modes that the attached PHY
++ * will switch between depending on media speed.
+ * @skb: Netlink message for cable diagnostics
+ * @nest: Netlink nest used for cable diagnostics
+ * @ehdr: nNtlink header for cable diagnostics
+@@ -665,6 +667,7 @@ struct phy_device {
+ u32 dev_flags;
+
+ phy_interface_t interface;
++ DECLARE_PHY_INTERFACE_MASK(possible_interfaces);
+
+ /*
+ * forced speed & duplex (no autoneg)
--- /dev/null
+From 85631f5b33f2acce7d42dec1d0a062ab40de95b8 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Sun, 19 Nov 2023 21:07:43 +0000
+Subject: [PATCH 2/7] net: phylink: use for_each_set_bit()
+
+Use for_each_set_bit() rather than open coding the for() test_bit()
+loop.
+
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Reviewed-by: Wojciech Drewek <wojciech.drewek@intel.com>
+Link: https://lore.kernel.org/r/E1r4p15-00Cpxe-C7@rmk-PC.armlinux.org.uk
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/phy/phylink.c | 18 ++++++++----------
+ 1 file changed, 8 insertions(+), 10 deletions(-)
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -690,18 +690,16 @@ static int phylink_validate_mask(struct
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(all_s) = { 0, };
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(s);
+ struct phylink_link_state t;
+- int intf;
++ int interface;
+
+- for (intf = 0; intf < PHY_INTERFACE_MODE_MAX; intf++) {
+- if (test_bit(intf, interfaces)) {
+- linkmode_copy(s, supported);
++ for_each_set_bit(interface, interfaces, PHY_INTERFACE_MODE_MAX) {
++ linkmode_copy(s, supported);
+
+- t = *state;
+- t.interface = intf;
+- if (!phylink_validate_mac_and_pcs(pl, s, &t)) {
+- linkmode_or(all_s, all_s, s);
+- linkmode_or(all_adv, all_adv, t.advertising);
+- }
++ t = *state;
++ t.interface = interface;
++ if (!phylink_validate_mac_and_pcs(pl, s, &t)) {
++ linkmode_or(all_s, all_s, s);
++ linkmode_or(all_adv, all_adv, t.advertising);
+ }
+ }
+
--- /dev/null
+From d4788b4383ce5caeb4e68818357c81a02117a3f9 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Fri, 24 Nov 2023 12:28:19 +0000
+Subject: [PATCH 3/7] net: phylink: split out per-interface validation
+
+Split out the internals of phylink_validate_mask() to make the code
+easier to read.
+
+Tested-by: Luo Jie <quic_luoj@quicinc.com>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/E1r6VIB-00DDLr-7g@rmk-PC.armlinux.org.uk
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/phylink.c | 42 ++++++++++++++++++++++++++++-----------
+ 1 file changed, 30 insertions(+), 12 deletions(-)
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -682,26 +682,44 @@ static int phylink_validate_mac_and_pcs(
+ return phylink_is_empty_linkmode(supported) ? -EINVAL : 0;
+ }
+
++static void phylink_validate_one(struct phylink *pl,
++ const unsigned long *supported,
++ const struct phylink_link_state *state,
++ phy_interface_t interface,
++ unsigned long *accum_supported,
++ unsigned long *accum_advertising)
++{
++ __ETHTOOL_DECLARE_LINK_MODE_MASK(tmp_supported);
++ struct phylink_link_state tmp_state;
++
++ linkmode_copy(tmp_supported, supported);
++
++ tmp_state = *state;
++ tmp_state.interface = interface;
++
++ if (!phylink_validate_mac_and_pcs(pl, tmp_supported, &tmp_state)) {
++ phylink_dbg(pl, " interface %u (%s) rate match %s supports %*pbl\n",
++ interface, phy_modes(interface),
++ phy_rate_matching_to_str(tmp_state.rate_matching),
++ __ETHTOOL_LINK_MODE_MASK_NBITS, tmp_supported);
++
++ linkmode_or(accum_supported, accum_supported, tmp_supported);
++ linkmode_or(accum_advertising, accum_advertising,
++ tmp_state.advertising);
++ }
++}
++
+ static int phylink_validate_mask(struct phylink *pl, unsigned long *supported,
+ struct phylink_link_state *state,
+ const unsigned long *interfaces)
+ {
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(all_adv) = { 0, };
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(all_s) = { 0, };
+- __ETHTOOL_DECLARE_LINK_MODE_MASK(s);
+- struct phylink_link_state t;
+ int interface;
+
+- for_each_set_bit(interface, interfaces, PHY_INTERFACE_MODE_MAX) {
+- linkmode_copy(s, supported);
+-
+- t = *state;
+- t.interface = interface;
+- if (!phylink_validate_mac_and_pcs(pl, s, &t)) {
+- linkmode_or(all_s, all_s, s);
+- linkmode_or(all_adv, all_adv, t.advertising);
+- }
+- }
++ for_each_set_bit(interface, interfaces, PHY_INTERFACE_MODE_MAX)
++ phylink_validate_one(pl, supported, state, interface,
++ all_s, all_adv);
+
+ linkmode_copy(supported, all_s);
+ linkmode_copy(state->advertising, all_adv);
--- /dev/null
+From ce7273c31fadb3143fc80c96a72a42adc19c2757 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Fri, 24 Nov 2023 12:28:24 +0000
+Subject: [PATCH 4/7] net: phylink: pass PHY into phylink_validate_one()
+
+Pass the phy (if any) into phylink_validate_one() so that we can
+validate each interface with its rate matching setting.
+
+Tested-by: Luo Jie <quic_luoj@quicinc.com>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/E1r6VIG-00DDLx-Cb@rmk-PC.armlinux.org.uk
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/phylink.c | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -682,7 +682,7 @@ static int phylink_validate_mac_and_pcs(
+ return phylink_is_empty_linkmode(supported) ? -EINVAL : 0;
+ }
+
+-static void phylink_validate_one(struct phylink *pl,
++static void phylink_validate_one(struct phylink *pl, struct phy_device *phy,
+ const unsigned long *supported,
+ const struct phylink_link_state *state,
+ phy_interface_t interface,
+@@ -697,6 +697,9 @@ static void phylink_validate_one(struct
+ tmp_state = *state;
+ tmp_state.interface = interface;
+
++ if (phy)
++ tmp_state.rate_matching = phy_get_rate_matching(phy, interface);
++
+ if (!phylink_validate_mac_and_pcs(pl, tmp_supported, &tmp_state)) {
+ phylink_dbg(pl, " interface %u (%s) rate match %s supports %*pbl\n",
+ interface, phy_modes(interface),
+@@ -718,7 +721,7 @@ static int phylink_validate_mask(struct
+ int interface;
+
+ for_each_set_bit(interface, interfaces, PHY_INTERFACE_MODE_MAX)
+- phylink_validate_one(pl, supported, state, interface,
++ phylink_validate_one(pl, NULL, supported, state, interface,
+ all_s, all_adv);
+
+ linkmode_copy(supported, all_s);
--- /dev/null
+From c6fec66d3cd76d797f70b30f1511bed10ba45a96 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Fri, 24 Nov 2023 12:28:29 +0000
+Subject: [PATCH 5/7] net: phylink: pass PHY into phylink_validate_mask()
+
+Pass the phy (if any) into phylink_validate_mask() so that we can
+validate each interface with its rate matching setting.
+
+Tested-by: Luo Jie <quic_luoj@quicinc.com>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/E1r6VIL-00DDM3-HJ@rmk-PC.armlinux.org.uk
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/phylink.c | 11 +++++++----
+ 1 file changed, 7 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -712,7 +712,8 @@ static void phylink_validate_one(struct
+ }
+ }
+
+-static int phylink_validate_mask(struct phylink *pl, unsigned long *supported,
++static int phylink_validate_mask(struct phylink *pl, struct phy_device *phy,
++ unsigned long *supported,
+ struct phylink_link_state *state,
+ const unsigned long *interfaces)
+ {
+@@ -721,7 +722,7 @@ static int phylink_validate_mask(struct
+ int interface;
+
+ for_each_set_bit(interface, interfaces, PHY_INTERFACE_MODE_MAX)
+- phylink_validate_one(pl, NULL, supported, state, interface,
++ phylink_validate_one(pl, phy, supported, state, interface,
+ all_s, all_adv);
+
+ linkmode_copy(supported, all_s);
+@@ -736,7 +737,8 @@ static int phylink_validate(struct phyli
+ const unsigned long *interfaces = pl->config->supported_interfaces;
+
+ if (state->interface == PHY_INTERFACE_MODE_NA)
+- return phylink_validate_mask(pl, supported, state, interfaces);
++ return phylink_validate_mask(pl, NULL, supported, state,
++ interfaces);
+
+ if (!test_bit(state->interface, interfaces))
+ return -EINVAL;
+@@ -3132,7 +3134,8 @@ static int phylink_sfp_config_optical(st
+ /* For all the interfaces that are supported, reduce the sfp_support
+ * mask to only those link modes that can be supported.
+ */
+- ret = phylink_validate_mask(pl, pl->sfp_support, &config, interfaces);
++ ret = phylink_validate_mask(pl, NULL, pl->sfp_support, &config,
++ interfaces);
+ if (ret) {
+ phylink_err(pl, "unsupported SFP module: validation with support %*pb failed\n",
+ __ETHTOOL_LINK_MODE_MASK_NBITS, support);
--- /dev/null
+From ee0e0ddb910e7e989b65a19d72b6435baa641fc7 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Fri, 24 Nov 2023 12:28:34 +0000
+Subject: [PATCH 6/7] net: phylink: split out PHY validation from
+ phylink_bringup_phy()
+
+When bringing up a PHY, we need to work out which ethtool link modes it
+should support and advertise. Clause 22 PHYs operate in a single
+interface mode, which can be easily dealt with. However, clause 45 PHYs
+tend to switch interface mode depending on the media. We need more
+flexible validation at this point, so this patch splits out that code
+in preparation to changing it.
+
+Tested-by: Luo Jie <quic_luoj@quicinc.com>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/E1r6VIQ-00DDM9-LK@rmk-PC.armlinux.org.uk
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/phylink.c | 56 ++++++++++++++++++++++-----------------
+ 1 file changed, 31 insertions(+), 25 deletions(-)
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -1738,6 +1738,35 @@ static void phylink_phy_change(struct ph
+ phylink_pause_to_str(pl->phy_state.pause));
+ }
+
++static int phylink_validate_phy(struct phylink *pl, struct phy_device *phy,
++ unsigned long *supported,
++ struct phylink_link_state *state)
++{
++ /* Check whether we would use rate matching for the proposed interface
++ * mode.
++ */
++ state->rate_matching = phy_get_rate_matching(phy, state->interface);
++
++ /* Clause 45 PHYs may switch their Serdes lane between, e.g. 10GBASE-R,
++ * 5GBASE-R, 2500BASE-X and SGMII if they are not using rate matching.
++ * For some interface modes (e.g. RXAUI, XAUI and USXGMII) switching
++ * their Serdes is either unnecessary or not reasonable.
++ *
++ * For these which switch interface modes, we really need to know which
++ * interface modes the PHY supports to properly work out which ethtool
++ * linkmodes can be supported. For now, as a work-around, we validate
++ * against all interface modes, which may lead to more ethtool link
++ * modes being advertised than are actually supported.
++ */
++ if (phy->is_c45 && state->rate_matching == RATE_MATCH_NONE &&
++ state->interface != PHY_INTERFACE_MODE_RXAUI &&
++ state->interface != PHY_INTERFACE_MODE_XAUI &&
++ state->interface != PHY_INTERFACE_MODE_USXGMII)
++ state->interface = PHY_INTERFACE_MODE_NA;
++
++ return phylink_validate(pl, supported, state);
++}
++
+ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
+ phy_interface_t interface)
+ {
+@@ -1758,32 +1787,9 @@ static int phylink_bringup_phy(struct ph
+ memset(&config, 0, sizeof(config));
+ linkmode_copy(supported, phy->supported);
+ linkmode_copy(config.advertising, phy->advertising);
++ config.interface = interface;
+
+- /* Check whether we would use rate matching for the proposed interface
+- * mode.
+- */
+- config.rate_matching = phy_get_rate_matching(phy, interface);
+-
+- /* Clause 45 PHYs may switch their Serdes lane between, e.g. 10GBASE-R,
+- * 5GBASE-R, 2500BASE-X and SGMII if they are not using rate matching.
+- * For some interface modes (e.g. RXAUI, XAUI and USXGMII) switching
+- * their Serdes is either unnecessary or not reasonable.
+- *
+- * For these which switch interface modes, we really need to know which
+- * interface modes the PHY supports to properly work out which ethtool
+- * linkmodes can be supported. For now, as a work-around, we validate
+- * against all interface modes, which may lead to more ethtool link
+- * modes being advertised than are actually supported.
+- */
+- if (phy->is_c45 && config.rate_matching == RATE_MATCH_NONE &&
+- 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);
++ ret = phylink_validate_phy(pl, phy, supported, &config);
+ if (ret) {
+ phylink_warn(pl, "validation of %s with support %*pb and advertisement %*pb failed: %pe\n",
+ phy_modes(config.interface),
--- /dev/null
+From 8f7a9799c5949f94ecc3acfd71b36437a7ade73b Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Fri, 24 Nov 2023 12:28:39 +0000
+Subject: [PATCH 7/7] net: phylink: use the PHY's possible_interfaces if
+ populated
+
+Some PHYs such as Aquantia, Broadcom 84881, and Marvell 88X33x0 can
+switch between a set of interface types depending on the negotiated
+media speed, or can use rate adaption for some or all of these
+interface types.
+
+We currently assume that these are Clause 45 PHYs that are configured
+not to use a specific set of interface modes, which has worked so far,
+but is just a work-around. In this workaround, we validate using all
+interfaces that the MAC supports, which can lead to extra modes being
+advertised that can not be supported.
+
+To properly address this, switch to using the newly introduced PHY
+possible_interfaces bitmap which indicates which interface modes will
+be used by the PHY as configured. We calculate the union of the PHY's
+possible interfaces and MACs supported interfaces, checking that is
+non-empty. If the PHY is on a SFP, we further reduce the set by those
+which can be used on a SFP module, again checking that is non-empty.
+Finally, we validate the subset of interfaces, taking account of
+whether rate matching will be used for each individual interface mode.
+
+This becomes independent of whether the PHY is clause 22 or clause 45.
+
+It is encouraged that all PHYs that switch interface modes or use
+rate matching should populate phydev->possible_interfaces.
+
+Tested-by: Luo Jie <quic_luoj@quicinc.com>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/E1r6VIV-00DDMF-Pi@rmk-PC.armlinux.org.uk
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/phylink.c | 67 +++++++++++++++++++++++++++++++--------
+ 1 file changed, 54 insertions(+), 13 deletions(-)
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -121,6 +121,19 @@ do { \
+ })
+ #endif
+
++static const phy_interface_t phylink_sfp_interface_preference[] = {
++ PHY_INTERFACE_MODE_25GBASER,
++ PHY_INTERFACE_MODE_USXGMII,
++ PHY_INTERFACE_MODE_10GBASER,
++ PHY_INTERFACE_MODE_5GBASER,
++ PHY_INTERFACE_MODE_2500BASEX,
++ PHY_INTERFACE_MODE_SGMII,
++ PHY_INTERFACE_MODE_1000BASEX,
++ PHY_INTERFACE_MODE_100BASEX,
++};
++
++static DECLARE_PHY_INTERFACE_MASK(phylink_sfp_interfaces);
++
+ /**
+ * phylink_set_port_modes() - set the port type modes in the ethtool mask
+ * @mask: ethtool link mode mask
+@@ -1742,6 +1755,47 @@ static int phylink_validate_phy(struct p
+ unsigned long *supported,
+ struct phylink_link_state *state)
+ {
++ DECLARE_PHY_INTERFACE_MASK(interfaces);
++
++ /* If the PHY provides a bitmap of the interfaces it will be using
++ * depending on the negotiated media speeds, use this to validate
++ * which ethtool link modes can be used.
++ */
++ if (!phy_interface_empty(phy->possible_interfaces)) {
++ /* We only care about the union of the PHY's interfaces and
++ * those which the host supports.
++ */
++ phy_interface_and(interfaces, phy->possible_interfaces,
++ pl->config->supported_interfaces);
++
++ if (phy_interface_empty(interfaces)) {
++ phylink_err(pl, "PHY has no common interfaces\n");
++ return -EINVAL;
++ }
++
++ if (phy_on_sfp(phy)) {
++ /* If the PHY is on a SFP, limit the interfaces to
++ * those that can be used with a SFP module.
++ */
++ phy_interface_and(interfaces, interfaces,
++ phylink_sfp_interfaces);
++
++ if (phy_interface_empty(interfaces)) {
++ phylink_err(pl, "SFP PHY's possible interfaces becomes empty\n");
++ return -EINVAL;
++ }
++ }
++
++ phylink_dbg(pl, "PHY %s uses interfaces %*pbl, validating %*pbl\n",
++ phydev_name(phy),
++ (int)PHY_INTERFACE_MODE_MAX,
++ phy->possible_interfaces,
++ (int)PHY_INTERFACE_MODE_MAX, interfaces);
++
++ return phylink_validate_mask(pl, phy, supported, state,
++ interfaces);
++ }
++
+ /* Check whether we would use rate matching for the proposed interface
+ * mode.
+ */
+@@ -2985,19 +3039,6 @@ static void phylink_sfp_detach(void *ups
+ pl->netdev->sfp_bus = NULL;
+ }
+
+-static const phy_interface_t phylink_sfp_interface_preference[] = {
+- PHY_INTERFACE_MODE_25GBASER,
+- PHY_INTERFACE_MODE_USXGMII,
+- PHY_INTERFACE_MODE_10GBASER,
+- PHY_INTERFACE_MODE_5GBASER,
+- PHY_INTERFACE_MODE_2500BASEX,
+- PHY_INTERFACE_MODE_SGMII,
+- PHY_INTERFACE_MODE_1000BASEX,
+- PHY_INTERFACE_MODE_100BASEX,
+-};
+-
+-static DECLARE_PHY_INTERFACE_MASK(phylink_sfp_interfaces);
+-
+ static phy_interface_t phylink_choose_sfp_interface(struct phylink *pl,
+ const unsigned long *intf)
+ {
--- /dev/null
+From f058b2dd70b1a5503dff899010aeb53b436091e5 Mon Sep 17 00:00:00 2001
+From: Robert Marko <robimarko@gmail.com>
+Date: Wed, 28 Feb 2024 18:24:09 +0100
+Subject: [PATCH 1/2] net: phy: qcom: qca808x: add helper for checking for 1G
+ only model
+
+There are 2 versions of QCA808x, one 2.5G capable and one 1G capable.
+Currently, this matter only in the .get_features call however, it will
+be required for filling supported interface modes so lets add a helper
+that can be reused.
+
+Signed-off-by: Robert Marko <robimarko@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/qcom/qca808x.c | 17 ++++++++++++-----
+ 1 file changed, 12 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/phy/qcom/qca808x.c
++++ b/drivers/net/phy/qcom/qca808x.c
+@@ -156,6 +156,17 @@ static bool qca808x_has_fast_retrain_or_
+ return linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);
+ }
+
++static bool qca808x_is_1g_only(struct phy_device *phydev)
++{
++ int ret;
++
++ ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_CHIP_TYPE);
++ if (ret < 0)
++ return true;
++
++ return !!(QCA808X_PHY_CHIP_TYPE_1G & ret);
++}
++
+ static int qca808x_probe(struct phy_device *phydev)
+ {
+ struct device *dev = &phydev->mdio.dev;
+@@ -350,11 +361,7 @@ static int qca808x_get_features(struct p
+ * existed in the bit0 of MMD1.21, we need to remove it manually if
+ * it is the qca8081 1G chip according to the bit0 of MMD7.0x901d.
+ */
+- ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_CHIP_TYPE);
+- if (ret < 0)
+- return ret;
+-
+- if (QCA808X_PHY_CHIP_TYPE_1G & ret)
++ if (qca808x_is_1g_only(phydev))
+ linkmode_clear_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);
+
+ return 0;
--- /dev/null
+From cb28f702960695e26597c332b0e46776e825cc34 Mon Sep 17 00:00:00 2001
+From: Robert Marko <robimarko@gmail.com>
+Date: Wed, 28 Feb 2024 18:24:10 +0100
+Subject: [PATCH 2/2] net: phy: qcom: qca808x: fill in possible_interfaces
+
+Currently QCA808x driver does not fill the possible_interfaces.
+2.5G QCA808x support SGMII and 2500Base-X while 1G model only supports
+SGMII, so fill the possible_interfaces accordingly.
+
+Signed-off-by: Robert Marko <robimarko@gmail.com>
+Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/qcom/qca808x.c | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+--- a/drivers/net/phy/qcom/qca808x.c
++++ b/drivers/net/phy/qcom/qca808x.c
+@@ -167,6 +167,16 @@ static bool qca808x_is_1g_only(struct ph
+ return !!(QCA808X_PHY_CHIP_TYPE_1G & ret);
+ }
+
++static void qca808x_fill_possible_interfaces(struct phy_device *phydev)
++{
++ unsigned long *possible = phydev->possible_interfaces;
++
++ __set_bit(PHY_INTERFACE_MODE_SGMII, possible);
++
++ if (!qca808x_is_1g_only(phydev))
++ __set_bit(PHY_INTERFACE_MODE_2500BASEX, possible);
++}
++
+ static int qca808x_probe(struct phy_device *phydev)
+ {
+ struct device *dev = &phydev->mdio.dev;
+@@ -231,6 +241,8 @@ static int qca808x_config_init(struct ph
+ }
+ }
+
++ qca808x_fill_possible_interfaces(phydev);
++
+ /* Configure adc threshold as 100mv for the link 10M */
+ return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD,
+ QCA808X_ADC_THRESHOLD_MASK,
--- /dev/null
+From 1a7aa058bc92f0edae7a0d1ef1a7b05aec0c643a Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Fri, 24 Nov 2023 12:27:52 +0000
+Subject: [PATCH 1/7] net: phy: add possible interfaces
+
+Add a possible_interfaces member to struct phy_device to indicate which
+interfaces a clause 45 PHY may switch between depending on the media.
+This must be populated by the PHY driver by the time the .config_init()
+method completes according to the PHYs host-side configuration.
+
+For example, the Marvell 88x3310 PHY can switch between 10GBASE-R,
+5GBASE-R, 2500BASE-X, and SGMII on the host side depending on the media
+side speed, so all these interface modes are set in the
+possible_interfaces member.
+
+This allows phylib users (such as phylink) to know in advance which
+interface modes to expect, which allows them to appropriately restrict
+the advertised link modes according to the capabilities of other parts
+of the link.
+
+Tested-by: Luo Jie <quic_luoj@quicinc.com>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/E1r6VHk-00DDLN-I7@rmk-PC.armlinux.org.uk
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/phy_device.c | 2 ++
+ include/linux/phy.h | 3 +++
+ 2 files changed, 5 insertions(+)
+
+--- a/drivers/net/phy/phy_device.c
++++ b/drivers/net/phy/phy_device.c
+@@ -1247,6 +1247,8 @@ int phy_init_hw(struct phy_device *phyde
+ if (ret < 0)
+ return ret;
+
++ phy_interface_zero(phydev->possible_interfaces);
++
+ if (phydev->drv->config_init) {
+ ret = phydev->drv->config_init(phydev);
+ if (ret < 0)
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -609,6 +609,8 @@ struct macsec_ops;
+ * @irq_rerun: Flag indicating interrupts occurred while PHY was suspended,
+ * requiring a rerun of the interrupt handler after resume
+ * @interface: enum phy_interface_t value
++ * @possible_interfaces: bitmap if interface modes that the attached PHY
++ * will switch between depending on media speed.
+ * @skb: Netlink message for cable diagnostics
+ * @nest: Netlink nest used for cable diagnostics
+ * @ehdr: nNtlink header for cable diagnostics
+@@ -678,6 +680,7 @@ struct phy_device {
+ u32 dev_flags;
+
+ phy_interface_t interface;
++ DECLARE_PHY_INTERFACE_MASK(possible_interfaces);
+
+ /*
+ * forced speed & duplex (no autoneg)
--- /dev/null
+From 85631f5b33f2acce7d42dec1d0a062ab40de95b8 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Sun, 19 Nov 2023 21:07:43 +0000
+Subject: [PATCH 2/7] net: phylink: use for_each_set_bit()
+
+Use for_each_set_bit() rather than open coding the for() test_bit()
+loop.
+
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Reviewed-by: Wojciech Drewek <wojciech.drewek@intel.com>
+Link: https://lore.kernel.org/r/E1r4p15-00Cpxe-C7@rmk-PC.armlinux.org.uk
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+---
+ drivers/net/phy/phylink.c | 18 ++++++++----------
+ 1 file changed, 8 insertions(+), 10 deletions(-)
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -712,18 +712,16 @@ static int phylink_validate_mask(struct
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(all_s) = { 0, };
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(s);
+ struct phylink_link_state t;
+- int intf;
++ int interface;
+
+- for (intf = 0; intf < PHY_INTERFACE_MODE_MAX; intf++) {
+- if (test_bit(intf, interfaces)) {
+- linkmode_copy(s, supported);
++ for_each_set_bit(interface, interfaces, PHY_INTERFACE_MODE_MAX) {
++ linkmode_copy(s, supported);
+
+- t = *state;
+- t.interface = intf;
+- if (!phylink_validate_mac_and_pcs(pl, s, &t)) {
+- linkmode_or(all_s, all_s, s);
+- linkmode_or(all_adv, all_adv, t.advertising);
+- }
++ t = *state;
++ t.interface = interface;
++ if (!phylink_validate_mac_and_pcs(pl, s, &t)) {
++ linkmode_or(all_s, all_s, s);
++ linkmode_or(all_adv, all_adv, t.advertising);
+ }
+ }
+
--- /dev/null
+From d4788b4383ce5caeb4e68818357c81a02117a3f9 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Fri, 24 Nov 2023 12:28:19 +0000
+Subject: [PATCH 3/7] net: phylink: split out per-interface validation
+
+Split out the internals of phylink_validate_mask() to make the code
+easier to read.
+
+Tested-by: Luo Jie <quic_luoj@quicinc.com>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/E1r6VIB-00DDLr-7g@rmk-PC.armlinux.org.uk
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/phylink.c | 42 ++++++++++++++++++++++++++++-----------
+ 1 file changed, 30 insertions(+), 12 deletions(-)
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -704,26 +704,44 @@ static int phylink_validate_mac_and_pcs(
+ return phylink_is_empty_linkmode(supported) ? -EINVAL : 0;
+ }
+
++static void phylink_validate_one(struct phylink *pl,
++ const unsigned long *supported,
++ const struct phylink_link_state *state,
++ phy_interface_t interface,
++ unsigned long *accum_supported,
++ unsigned long *accum_advertising)
++{
++ __ETHTOOL_DECLARE_LINK_MODE_MASK(tmp_supported);
++ struct phylink_link_state tmp_state;
++
++ linkmode_copy(tmp_supported, supported);
++
++ tmp_state = *state;
++ tmp_state.interface = interface;
++
++ if (!phylink_validate_mac_and_pcs(pl, tmp_supported, &tmp_state)) {
++ phylink_dbg(pl, " interface %u (%s) rate match %s supports %*pbl\n",
++ interface, phy_modes(interface),
++ phy_rate_matching_to_str(tmp_state.rate_matching),
++ __ETHTOOL_LINK_MODE_MASK_NBITS, tmp_supported);
++
++ linkmode_or(accum_supported, accum_supported, tmp_supported);
++ linkmode_or(accum_advertising, accum_advertising,
++ tmp_state.advertising);
++ }
++}
++
+ static int phylink_validate_mask(struct phylink *pl, unsigned long *supported,
+ struct phylink_link_state *state,
+ const unsigned long *interfaces)
+ {
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(all_adv) = { 0, };
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(all_s) = { 0, };
+- __ETHTOOL_DECLARE_LINK_MODE_MASK(s);
+- struct phylink_link_state t;
+ int interface;
+
+- for_each_set_bit(interface, interfaces, PHY_INTERFACE_MODE_MAX) {
+- linkmode_copy(s, supported);
+-
+- t = *state;
+- t.interface = interface;
+- if (!phylink_validate_mac_and_pcs(pl, s, &t)) {
+- linkmode_or(all_s, all_s, s);
+- linkmode_or(all_adv, all_adv, t.advertising);
+- }
+- }
++ for_each_set_bit(interface, interfaces, PHY_INTERFACE_MODE_MAX)
++ phylink_validate_one(pl, supported, state, interface,
++ all_s, all_adv);
+
+ linkmode_copy(supported, all_s);
+ linkmode_copy(state->advertising, all_adv);
--- /dev/null
+From ce7273c31fadb3143fc80c96a72a42adc19c2757 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Fri, 24 Nov 2023 12:28:24 +0000
+Subject: [PATCH 4/7] net: phylink: pass PHY into phylink_validate_one()
+
+Pass the phy (if any) into phylink_validate_one() so that we can
+validate each interface with its rate matching setting.
+
+Tested-by: Luo Jie <quic_luoj@quicinc.com>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/E1r6VIG-00DDLx-Cb@rmk-PC.armlinux.org.uk
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/phylink.c | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -704,7 +704,7 @@ static int phylink_validate_mac_and_pcs(
+ return phylink_is_empty_linkmode(supported) ? -EINVAL : 0;
+ }
+
+-static void phylink_validate_one(struct phylink *pl,
++static void phylink_validate_one(struct phylink *pl, struct phy_device *phy,
+ const unsigned long *supported,
+ const struct phylink_link_state *state,
+ phy_interface_t interface,
+@@ -719,6 +719,9 @@ static void phylink_validate_one(struct
+ tmp_state = *state;
+ tmp_state.interface = interface;
+
++ if (phy)
++ tmp_state.rate_matching = phy_get_rate_matching(phy, interface);
++
+ if (!phylink_validate_mac_and_pcs(pl, tmp_supported, &tmp_state)) {
+ phylink_dbg(pl, " interface %u (%s) rate match %s supports %*pbl\n",
+ interface, phy_modes(interface),
+@@ -740,7 +743,7 @@ static int phylink_validate_mask(struct
+ int interface;
+
+ for_each_set_bit(interface, interfaces, PHY_INTERFACE_MODE_MAX)
+- phylink_validate_one(pl, supported, state, interface,
++ phylink_validate_one(pl, NULL, supported, state, interface,
+ all_s, all_adv);
+
+ linkmode_copy(supported, all_s);
--- /dev/null
+From c6fec66d3cd76d797f70b30f1511bed10ba45a96 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Fri, 24 Nov 2023 12:28:29 +0000
+Subject: [PATCH 5/7] net: phylink: pass PHY into phylink_validate_mask()
+
+Pass the phy (if any) into phylink_validate_mask() so that we can
+validate each interface with its rate matching setting.
+
+Tested-by: Luo Jie <quic_luoj@quicinc.com>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/E1r6VIL-00DDM3-HJ@rmk-PC.armlinux.org.uk
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/phylink.c | 11 +++++++----
+ 1 file changed, 7 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -734,7 +734,8 @@ static void phylink_validate_one(struct
+ }
+ }
+
+-static int phylink_validate_mask(struct phylink *pl, unsigned long *supported,
++static int phylink_validate_mask(struct phylink *pl, struct phy_device *phy,
++ unsigned long *supported,
+ struct phylink_link_state *state,
+ const unsigned long *interfaces)
+ {
+@@ -743,7 +744,7 @@ static int phylink_validate_mask(struct
+ int interface;
+
+ for_each_set_bit(interface, interfaces, PHY_INTERFACE_MODE_MAX)
+- phylink_validate_one(pl, NULL, supported, state, interface,
++ phylink_validate_one(pl, phy, supported, state, interface,
+ all_s, all_adv);
+
+ linkmode_copy(supported, all_s);
+@@ -758,7 +759,8 @@ static int phylink_validate(struct phyli
+ const unsigned long *interfaces = pl->config->supported_interfaces;
+
+ if (state->interface == PHY_INTERFACE_MODE_NA)
+- return phylink_validate_mask(pl, supported, state, interfaces);
++ return phylink_validate_mask(pl, NULL, supported, state,
++ interfaces);
+
+ if (!test_bit(state->interface, interfaces))
+ return -EINVAL;
+@@ -3194,7 +3196,8 @@ static int phylink_sfp_config_optical(st
+ /* For all the interfaces that are supported, reduce the sfp_support
+ * mask to only those link modes that can be supported.
+ */
+- ret = phylink_validate_mask(pl, pl->sfp_support, &config, interfaces);
++ ret = phylink_validate_mask(pl, NULL, pl->sfp_support, &config,
++ interfaces);
+ if (ret) {
+ phylink_err(pl, "unsupported SFP module: validation with support %*pb failed\n",
+ __ETHTOOL_LINK_MODE_MASK_NBITS, support);
--- /dev/null
+From ee0e0ddb910e7e989b65a19d72b6435baa641fc7 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Fri, 24 Nov 2023 12:28:34 +0000
+Subject: [PATCH 6/7] net: phylink: split out PHY validation from
+ phylink_bringup_phy()
+
+When bringing up a PHY, we need to work out which ethtool link modes it
+should support and advertise. Clause 22 PHYs operate in a single
+interface mode, which can be easily dealt with. However, clause 45 PHYs
+tend to switch interface mode depending on the media. We need more
+flexible validation at this point, so this patch splits out that code
+in preparation to changing it.
+
+Tested-by: Luo Jie <quic_luoj@quicinc.com>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/E1r6VIQ-00DDM9-LK@rmk-PC.armlinux.org.uk
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/phylink.c | 56 ++++++++++++++++++++++-----------------
+ 1 file changed, 31 insertions(+), 25 deletions(-)
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -1775,6 +1775,35 @@ static void phylink_phy_change(struct ph
+ phylink_pause_to_str(pl->phy_state.pause));
+ }
+
++static int phylink_validate_phy(struct phylink *pl, struct phy_device *phy,
++ unsigned long *supported,
++ struct phylink_link_state *state)
++{
++ /* Check whether we would use rate matching for the proposed interface
++ * mode.
++ */
++ state->rate_matching = phy_get_rate_matching(phy, state->interface);
++
++ /* Clause 45 PHYs may switch their Serdes lane between, e.g. 10GBASE-R,
++ * 5GBASE-R, 2500BASE-X and SGMII if they are not using rate matching.
++ * For some interface modes (e.g. RXAUI, XAUI and USXGMII) switching
++ * their Serdes is either unnecessary or not reasonable.
++ *
++ * For these which switch interface modes, we really need to know which
++ * interface modes the PHY supports to properly work out which ethtool
++ * linkmodes can be supported. For now, as a work-around, we validate
++ * against all interface modes, which may lead to more ethtool link
++ * modes being advertised than are actually supported.
++ */
++ if (phy->is_c45 && state->rate_matching == RATE_MATCH_NONE &&
++ state->interface != PHY_INTERFACE_MODE_RXAUI &&
++ state->interface != PHY_INTERFACE_MODE_XAUI &&
++ state->interface != PHY_INTERFACE_MODE_USXGMII)
++ state->interface = PHY_INTERFACE_MODE_NA;
++
++ return phylink_validate(pl, supported, state);
++}
++
+ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
+ phy_interface_t interface)
+ {
+@@ -1795,32 +1824,9 @@ static int phylink_bringup_phy(struct ph
+ memset(&config, 0, sizeof(config));
+ linkmode_copy(supported, phy->supported);
+ linkmode_copy(config.advertising, phy->advertising);
++ config.interface = interface;
+
+- /* Check whether we would use rate matching for the proposed interface
+- * mode.
+- */
+- config.rate_matching = phy_get_rate_matching(phy, interface);
+-
+- /* Clause 45 PHYs may switch their Serdes lane between, e.g. 10GBASE-R,
+- * 5GBASE-R, 2500BASE-X and SGMII if they are not using rate matching.
+- * For some interface modes (e.g. RXAUI, XAUI and USXGMII) switching
+- * their Serdes is either unnecessary or not reasonable.
+- *
+- * For these which switch interface modes, we really need to know which
+- * interface modes the PHY supports to properly work out which ethtool
+- * linkmodes can be supported. For now, as a work-around, we validate
+- * against all interface modes, which may lead to more ethtool link
+- * modes being advertised than are actually supported.
+- */
+- if (phy->is_c45 && config.rate_matching == RATE_MATCH_NONE &&
+- 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);
++ ret = phylink_validate_phy(pl, phy, supported, &config);
+ if (ret) {
+ phylink_warn(pl, "validation of %s with support %*pb and advertisement %*pb failed: %pe\n",
+ phy_modes(config.interface),
--- /dev/null
+From 8f7a9799c5949f94ecc3acfd71b36437a7ade73b Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Fri, 24 Nov 2023 12:28:39 +0000
+Subject: [PATCH 7/7] net: phylink: use the PHY's possible_interfaces if
+ populated
+
+Some PHYs such as Aquantia, Broadcom 84881, and Marvell 88X33x0 can
+switch between a set of interface types depending on the negotiated
+media speed, or can use rate adaption for some or all of these
+interface types.
+
+We currently assume that these are Clause 45 PHYs that are configured
+not to use a specific set of interface modes, which has worked so far,
+but is just a work-around. In this workaround, we validate using all
+interfaces that the MAC supports, which can lead to extra modes being
+advertised that can not be supported.
+
+To properly address this, switch to using the newly introduced PHY
+possible_interfaces bitmap which indicates which interface modes will
+be used by the PHY as configured. We calculate the union of the PHY's
+possible interfaces and MACs supported interfaces, checking that is
+non-empty. If the PHY is on a SFP, we further reduce the set by those
+which can be used on a SFP module, again checking that is non-empty.
+Finally, we validate the subset of interfaces, taking account of
+whether rate matching will be used for each individual interface mode.
+
+This becomes independent of whether the PHY is clause 22 or clause 45.
+
+It is encouraged that all PHYs that switch interface modes or use
+rate matching should populate phydev->possible_interfaces.
+
+Tested-by: Luo Jie <quic_luoj@quicinc.com>
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/E1r6VIV-00DDMF-Pi@rmk-PC.armlinux.org.uk
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/phylink.c | 67 +++++++++++++++++++++++++++++++--------
+ 1 file changed, 54 insertions(+), 13 deletions(-)
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -121,6 +121,19 @@ do { \
+ })
+ #endif
+
++static const phy_interface_t phylink_sfp_interface_preference[] = {
++ PHY_INTERFACE_MODE_25GBASER,
++ PHY_INTERFACE_MODE_USXGMII,
++ PHY_INTERFACE_MODE_10GBASER,
++ PHY_INTERFACE_MODE_5GBASER,
++ PHY_INTERFACE_MODE_2500BASEX,
++ PHY_INTERFACE_MODE_SGMII,
++ PHY_INTERFACE_MODE_1000BASEX,
++ PHY_INTERFACE_MODE_100BASEX,
++};
++
++static DECLARE_PHY_INTERFACE_MASK(phylink_sfp_interfaces);
++
+ /**
+ * phylink_set_port_modes() - set the port type modes in the ethtool mask
+ * @mask: ethtool link mode mask
+@@ -1779,6 +1792,47 @@ static int phylink_validate_phy(struct p
+ unsigned long *supported,
+ struct phylink_link_state *state)
+ {
++ DECLARE_PHY_INTERFACE_MASK(interfaces);
++
++ /* If the PHY provides a bitmap of the interfaces it will be using
++ * depending on the negotiated media speeds, use this to validate
++ * which ethtool link modes can be used.
++ */
++ if (!phy_interface_empty(phy->possible_interfaces)) {
++ /* We only care about the union of the PHY's interfaces and
++ * those which the host supports.
++ */
++ phy_interface_and(interfaces, phy->possible_interfaces,
++ pl->config->supported_interfaces);
++
++ if (phy_interface_empty(interfaces)) {
++ phylink_err(pl, "PHY has no common interfaces\n");
++ return -EINVAL;
++ }
++
++ if (phy_on_sfp(phy)) {
++ /* If the PHY is on a SFP, limit the interfaces to
++ * those that can be used with a SFP module.
++ */
++ phy_interface_and(interfaces, interfaces,
++ phylink_sfp_interfaces);
++
++ if (phy_interface_empty(interfaces)) {
++ phylink_err(pl, "SFP PHY's possible interfaces becomes empty\n");
++ return -EINVAL;
++ }
++ }
++
++ phylink_dbg(pl, "PHY %s uses interfaces %*pbl, validating %*pbl\n",
++ phydev_name(phy),
++ (int)PHY_INTERFACE_MODE_MAX,
++ phy->possible_interfaces,
++ (int)PHY_INTERFACE_MODE_MAX, interfaces);
++
++ return phylink_validate_mask(pl, phy, supported, state,
++ interfaces);
++ }
++
+ /* Check whether we would use rate matching for the proposed interface
+ * mode.
+ */
+@@ -3047,19 +3101,6 @@ static void phylink_sfp_detach(void *ups
+ pl->netdev->sfp_bus = NULL;
+ }
+
+-static const phy_interface_t phylink_sfp_interface_preference[] = {
+- PHY_INTERFACE_MODE_25GBASER,
+- PHY_INTERFACE_MODE_USXGMII,
+- PHY_INTERFACE_MODE_10GBASER,
+- PHY_INTERFACE_MODE_5GBASER,
+- PHY_INTERFACE_MODE_2500BASEX,
+- PHY_INTERFACE_MODE_SGMII,
+- PHY_INTERFACE_MODE_1000BASEX,
+- PHY_INTERFACE_MODE_100BASEX,
+-};
+-
+-static DECLARE_PHY_INTERFACE_MASK(phylink_sfp_interfaces);
+-
+ static phy_interface_t phylink_choose_sfp_interface(struct phylink *pl,
+ const unsigned long *intf)
+ {
--- /dev/null
+From f058b2dd70b1a5503dff899010aeb53b436091e5 Mon Sep 17 00:00:00 2001
+From: Robert Marko <robimarko@gmail.com>
+Date: Wed, 28 Feb 2024 18:24:09 +0100
+Subject: [PATCH 1/2] net: phy: qcom: qca808x: add helper for checking for 1G
+ only model
+
+There are 2 versions of QCA808x, one 2.5G capable and one 1G capable.
+Currently, this matter only in the .get_features call however, it will
+be required for filling supported interface modes so lets add a helper
+that can be reused.
+
+Signed-off-by: Robert Marko <robimarko@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/qcom/qca808x.c | 17 ++++++++++++-----
+ 1 file changed, 12 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/phy/qcom/qca808x.c
++++ b/drivers/net/phy/qcom/qca808x.c
+@@ -156,6 +156,17 @@ static bool qca808x_has_fast_retrain_or_
+ return linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);
+ }
+
++static bool qca808x_is_1g_only(struct phy_device *phydev)
++{
++ int ret;
++
++ ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_CHIP_TYPE);
++ if (ret < 0)
++ return true;
++
++ return !!(QCA808X_PHY_CHIP_TYPE_1G & ret);
++}
++
+ static int qca808x_probe(struct phy_device *phydev)
+ {
+ struct device *dev = &phydev->mdio.dev;
+@@ -350,11 +361,7 @@ static int qca808x_get_features(struct p
+ * existed in the bit0 of MMD1.21, we need to remove it manually if
+ * it is the qca8081 1G chip according to the bit0 of MMD7.0x901d.
+ */
+- ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_CHIP_TYPE);
+- if (ret < 0)
+- return ret;
+-
+- if (QCA808X_PHY_CHIP_TYPE_1G & ret)
++ if (qca808x_is_1g_only(phydev))
+ linkmode_clear_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);
+
+ return 0;
--- /dev/null
+From cb28f702960695e26597c332b0e46776e825cc34 Mon Sep 17 00:00:00 2001
+From: Robert Marko <robimarko@gmail.com>
+Date: Wed, 28 Feb 2024 18:24:10 +0100
+Subject: [PATCH 2/2] net: phy: qcom: qca808x: fill in possible_interfaces
+
+Currently QCA808x driver does not fill the possible_interfaces.
+2.5G QCA808x support SGMII and 2500Base-X while 1G model only supports
+SGMII, so fill the possible_interfaces accordingly.
+
+Signed-off-by: Robert Marko <robimarko@gmail.com>
+Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/qcom/qca808x.c | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+--- a/drivers/net/phy/qcom/qca808x.c
++++ b/drivers/net/phy/qcom/qca808x.c
+@@ -167,6 +167,16 @@ static bool qca808x_is_1g_only(struct ph
+ return !!(QCA808X_PHY_CHIP_TYPE_1G & ret);
+ }
+
++static void qca808x_fill_possible_interfaces(struct phy_device *phydev)
++{
++ unsigned long *possible = phydev->possible_interfaces;
++
++ __set_bit(PHY_INTERFACE_MODE_SGMII, possible);
++
++ if (!qca808x_is_1g_only(phydev))
++ __set_bit(PHY_INTERFACE_MODE_2500BASEX, possible);
++}
++
+ static int qca808x_probe(struct phy_device *phydev)
+ {
+ struct device *dev = &phydev->mdio.dev;
+@@ -231,6 +241,8 @@ static int qca808x_config_init(struct ph
+ }
+ }
+
++ qca808x_fill_possible_interfaces(phydev);
++
+ /* Configure adc threshold as 100mv for the link 10M */
+ return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD,
+ QCA808X_ADC_THRESHOLD_MASK,
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
-@@ -1850,6 +1850,9 @@ void phy_detach(struct phy_device *phyde
+@@ -1852,6 +1852,9 @@ void phy_detach(struct phy_device *phyde
struct module *ndev_owner = NULL;
struct mii_bus *bus;
sysfs_remove_link(&dev->dev.kobj, "phydev");
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
-@@ -900,6 +900,12 @@ struct phy_driver {
+@@ -903,6 +903,12 @@ struct phy_driver {
/** @handle_interrupt: Override default interrupt handling */
irqreturn_t (*handle_interrupt)(struct phy_device *phydev);
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
-@@ -1908,6 +1908,9 @@ void phy_detach(struct phy_device *phyde
+@@ -1910,6 +1910,9 @@ void phy_detach(struct phy_device *phyde
if (phydev->devlink)
device_link_del(phydev->devlink);
sysfs_remove_link(&dev->dev.kobj, "phydev");
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
-@@ -976,6 +976,12 @@ struct phy_driver {
+@@ -979,6 +979,12 @@ struct phy_driver {
/** @handle_interrupt: Override default interrupt handling */
irqreturn_t (*handle_interrupt)(struct phy_device *phydev);
int phy_init_hw(struct phy_device *phydev)
{
int ret = 0;
-@@ -1259,6 +1290,12 @@ int phy_init_hw(struct phy_device *phyde
+@@ -1261,6 +1292,12 @@ int phy_init_hw(struct phy_device *phyde
return ret;
}
return 0;
}
EXPORT_SYMBOL(phy_init_hw);
-@@ -3204,7 +3241,6 @@ static int of_phy_led(struct phy_device
+@@ -3206,7 +3243,6 @@ static int of_phy_led(struct phy_device
struct device *dev = &phydev->mdio.dev;
struct led_init_data init_data = {};
struct led_classdev *cdev;
struct phy_led *phyled;
u32 index;
int err;
-@@ -3222,21 +3258,6 @@ static int of_phy_led(struct phy_device
+@@ -3224,21 +3260,6 @@ static int of_phy_led(struct phy_device
if (index > U8_MAX)
return -EINVAL;