diff mbox series

[net-next,6/6] net: phy: dp83869: Fix link up reporting in SGMII bridge mode

Message ID 20240701-b4-dp83869-sfp-v1-6-a71d6d0ad5f8@bootlin.com (mailing list archive)
State Changes Requested
Delegated to: Netdev Maintainers
Headers show
Series net: phy: dp83869: Add support for downstream SFP cages | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for net-next
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 839 this patch: 839
netdev/build_tools success No tools touched, skip
netdev/cc_maintainers success CCed 7 of 7 maintainers
netdev/build_clang success Errors and warnings before: 846 this patch: 846
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 846 this patch: 846
netdev/checkpatch warning WARNING: line length of 84 exceeds 80 columns
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0
netdev/contest success net-next-2024-07-01--15-00 (tests: 664)

Commit Message

Romain Gantois July 1, 2024, 8:51 a.m. UTC
In RGMII-SGMII bridge mode, the DP83869HM PHY can perform an SGMII
autonegotiation with a downstream link partner. In this operational mode,
the standard link and autonegotiation status bits do not report correct
values, the extended fiber status register must be checked.

Link parameters should theoretically be obtainable from the DP83869
registers after SGMII autonegotiation has completed. However, for unknown
reasons, this is not the case, and the link partner fiber capabilities
register will incorrectly report a remote fault even if the SGMII
autonegotiation was successful.

Modify the read_status() callback of the DP83869 driver to check the fiber
status register in RGMII-SGMII bridge mode. On link up, obtain the link
parameters from the downstream SFP PHY driver if possible. If not, set
speed and duplex to "unknown".

Signed-off-by: Romain Gantois <romain.gantois@bootlin.com>
---
 drivers/net/phy/dp83869.c | 40 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 39 insertions(+), 1 deletion(-)

Comments

Andrew Lunn July 1, 2024, 5:09 p.m. UTC | #1
> +			if (dp83869->mod_phy) {
> +				ret = phy_read_status(dp83869->mod_phy);
> +				if (ret)
> +					return ret;

Locking? When phylib does this in phy_check_link_status(), we have:

	lockdep_assert_held(&phydev->lock);

I don't see anything which takes the downstreams PHY lock.

Is this also introducing race conditions? What happens if the link
just went down? phy_check_link_status() takes actions. Will they still
happen when phylib next talks to the downstream PHY? It is probably
safer to call phy_check_link_status() than phy_read_status().

   Andrew
Romain Gantois July 2, 2024, 9:04 a.m. UTC | #2
On lundi 1 juillet 2024 19:09:40 UTC+2 Andrew Lunn wrote:
> > +			if (dp83869->mod_phy) {
> > +				ret = phy_read_status(dp83869->mod_phy);
> > +				if (ret)
> > +					return ret;
> 
> Locking? When phylib does this in phy_check_link_status(), we have:
> 
> 	lockdep_assert_held(&phydev->lock);
> 
> I don't see anything which takes the downstreams PHY lock.
> 
> Is this also introducing race conditions? What happens if the link
> just went down? phy_check_link_status() takes actions. Will they still
> happen when phylib next talks to the downstream PHY? It is probably
> safer to call phy_check_link_status() than phy_read_status().

Given that the phylib state machine will call phy_check_link_status() itself,
I think that this call to phy_read_status() could be dropped entirely and that
dp83869_read_status() could just directly read mod_phy->{link,speed,duplex}.

This raises the question of a potential race condition when reading
mod_phy->{link, speed, duplex}. I haven't seen any kind of locking used in
other parts of the net subsystem when reading these parameters.

Thanks,
Russell King (Oracle) July 2, 2024, 9:28 a.m. UTC | #3
On Tue, Jul 02, 2024 at 11:04:37AM +0200, Romain Gantois wrote:
> This raises the question of a potential race condition when reading
> mod_phy->{link, speed, duplex}. I haven't seen any kind of locking used in
> other parts of the net subsystem when reading these parameters.

Drivers access these under phydev->lock due to a callback from phylib
which will be made from the state machine which holds that lock.
This includes phylink.
diff mbox series

Patch

diff --git a/drivers/net/phy/dp83869.c b/drivers/net/phy/dp83869.c
index a07ec1be84baf..843af90667d41 100644
--- a/drivers/net/phy/dp83869.c
+++ b/drivers/net/phy/dp83869.c
@@ -43,6 +43,7 @@ 
 #define DP83869_IO_MUX_CFG	0x0170
 #define DP83869_OP_MODE		0x01df
 #define DP83869_FX_CTRL		0x0c00
+#define DP83869_FX_STS		0x0c01
 
 #define DP83869_SW_RESET	BIT(15)
 #define DP83869_SW_RESTART	BIT(14)
@@ -72,6 +73,10 @@ 
 /* This is the same bit mask as the BMCR so re-use the BMCR default */
 #define DP83869_FX_CTRL_DEFAULT	MII_DP83869_BMCR_DEFAULT
 
+/* FX_STS bits */
+#define DP83869_FX_LSTATUS         BIT(2)
+#define DP83869_FX_ANEGCOMPLETE    BIT(5)
+
 /* CFG1 bits */
 #define DP83869_CFG1_DEFAULT	(ADVERTISE_1000HALF | \
 				 ADVERTISE_1000FULL | \
@@ -160,7 +165,8 @@  struct dp83869_private {
 static int dp83869_read_status(struct phy_device *phydev)
 {
 	struct dp83869_private *dp83869 = phydev->priv;
-	int ret;
+	int ret, old_link = phydev->link;
+	u32 status;
 
 	ret = genphy_read_status(phydev);
 	if (ret)
@@ -176,6 +182,38 @@  static int dp83869_read_status(struct phy_device *phydev)
 		}
 	}
 
+	if (dp83869->mode == DP83869_RGMII_SGMII_BRIDGE) {
+		/* check if SGMII link is up */
+		status = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_FX_STS);
+
+		phydev->link = status & DP83869_FX_LSTATUS ? 1 : 0;
+		phydev->autoneg_complete = status & DP83869_FX_ANEGCOMPLETE ? 1 : 0;
+
+		if (!phydev->autoneg_complete) {
+			phydev->link = 0;
+		} else if (phydev->link && !old_link) {
+			/* It seems like link status and duplex resolved from
+			 * SGMII autonegotiation are incorrectly reported in
+			 * the fiber link partner capabilities register and in
+			 * the PHY status register. If there is a handle to the
+			 * downstream PHY, read link parameters from it. If
+			 * not, fallback to unknown.
+			 */
+
+			if (dp83869->mod_phy) {
+				ret = phy_read_status(dp83869->mod_phy);
+				if (ret)
+					return ret;
+
+				phydev->speed = dp83869->mod_phy->speed;
+				phydev->duplex = dp83869->mod_phy->duplex;
+			} else {
+				phydev->speed = SPEED_UNKNOWN;
+				phydev->duplex = DUPLEX_UNKNOWN;
+			}
+		}
+	}
+
 	return 0;
 }