[PATCH v2 net-next] net: phy: mdio-i2c: defer RollBall bridge probe to PHY discovery

Petr Wozniak posted 1 patch 3 days, 1 hour ago
drivers/net/mdio/mdio-i2c.c   | 18 ++++--------------
drivers/net/phy/sfp.c         | 18 ++++++++++++------
include/linux/mdio/mdio-i2c.h |  1 +
3 files changed, 17 insertions(+), 20 deletions(-)
[PATCH v2 net-next] net: phy: mdio-i2c: defer RollBall bridge probe to PHY discovery
Posted by Petr Wozniak 3 days, 1 hour ago
commit 8fe125892f40 ("net: phy: sfp: probe for RollBall I2C-to-MDIO
bridge in mdio-i2c") introduced a regression: the RollBall I2C-to-MDIO
bridge is not yet ready to respond to CMD_READ/CMD_DONE cycles when
sfp_sm_add_mdio_bus() runs in SFP_S_INIT.  The 200 ms probe times out,
i2c_mii_probe_rollball() returns -ENODEV, and sfp_sm_add_mdio_bus()
sets mdio_protocol = MDIO_I2C_NONE.  By the time sfp_sm_probe_for_phy()
runs (up to ~17 s later on affected hardware), the bridge is fully
initialized — but PHY probing is skipped because the protocol has
already been changed to NONE.

This affects both modules inserted before boot and hotplugged modules on
hardware where bridge initialization exceeds the 200 ms probe window
(confirmed: FLYPRO SFP-10GT-CS-30M with Aquantia AQR113C, hotplugged).

Move the probe from i2c_mii_init_rollball() — called at bus-creation
time — to sfp_sm_probe_for_phy() in sfp.c, where it runs after the SFP
state machine's module initialization delays.  Export the probe function
as mdio_i2c_probe_rollball() so sfp.c can call it.

For RTL8261BE-based modules: the probe correctly returns -ENODEV at PHY
discovery time, causing sfp_sm_probe_for_phy() to destroy the MDIO bus
and set MDIO_I2C_NONE — eliminating the 5+ minute PHY probe retry loop.

For genuine RollBall modules (e.g. FLYPRO SFP-10GT-CS-30M with Aquantia
AQR113C): the probe now runs after initialization is complete and
correctly returns 0 — PHY detection proceeds normally.

Reported-by: Aleksander Bajkowski <olek2@wp.pl>
Fixes: 8fe125892f40 ("net: phy: sfp: probe for RollBall I2C-to-MDIO bridge in mdio-i2c")
Signed-off-by: Petr Wozniak <petr.wozniak@gmail.com>
---
Changes in v2:
 - Generalize scope: regression affects both boot-inserted and hotplugged
   modules on hardware where bridge init exceeds 200 ms; Aleksander
   Bajkowski confirmed FLYPRO SFP-10GT-CS-30M / AQR113C broken even
   when hotplugged
 - Correct state machine description: probe runs in SFP_S_INIT (which
   follows SFP_S_WAIT), not "before initialization delays" (Jan Hoffmann)
 - No code changes from v1

 drivers/net/mdio/mdio-i2c.c   | 18 ++++--------------
 drivers/net/phy/sfp.c         | 18 ++++++++++++------
 include/linux/mdio/mdio-i2c.h |  1 +
 3 files changed, 17 insertions(+), 20 deletions(-)

diff --git a/drivers/net/mdio/mdio-i2c.c b/drivers/net/mdio/mdio-i2c.c
--- a/drivers/net/mdio/mdio-i2c.c
+++ b/drivers/net/mdio/mdio-i2c.c
@@ -352,7 +352,7 @@
 	return 0;
 }

-static int i2c_mii_probe_rollball(struct i2c_adapter *i2c)
+int mdio_i2c_probe_rollball(struct i2c_adapter *i2c)
 {
 	u8 data_buf[] = { ROLLBALL_DATA_ADDR, 0x01, 0x00, 0x00 };
 	u8 cmd_buf[]  = { ROLLBALL_CMD_ADDR, ROLLBALL_CMD_READ };
@@ -397,9 +397,11 @@

 	return -ENODEV;
 }
+EXPORT_SYMBOL_GPL(mdio_i2c_probe_rollball);

 static int i2c_mii_init_rollball(struct i2c_adapter *i2c)
 {
+	/* Send the RollBall unlock password; bridge presence is verified
+	 * later, in sfp_sm_probe_for_phy(), after module initialization. */
 	struct i2c_msg msg;
 	u8 pw[5];
 	int ret;
@@ -419,7 +421,7 @@
 	if (ret != 1)
 		return -EIO;

-	return i2c_mii_probe_rollball(i2c);
+	return 0;
 }

 struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c,
@@ -444,12 +446,8 @@
 	case MDIO_I2C_ROLLBALL:
 		ret = i2c_mii_init_rollball(i2c);
 		if (ret < 0) {
-			if (ret != -ENODEV)
-				dev_err(parent,
-					"Cannot initialize RollBall MDIO I2C protocol: %d\n",
-					ret);
-			/* -ENODEV propagates to caller: no bridge present,
-			 * PHY probing should be skipped for this module. */
+			dev_err(parent,
+				"Cannot initialize RollBall MDIO I2C protocol: %d\n",
+				ret);
 			mdiobus_free(mii);
 			return ERR_PTR(ret);
 		}
diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c
--- a/drivers/net/phy/sfp.c
+++ b/drivers/net/phy/sfp.c
@@ -2028,17 +2028,8 @@ static int sfp_sm_add_mdio_bus(struct sfp *sfp)
 	dev_info(sfp->dev, "probing phy device through the [%s] protocol\n",
 		 mdio_i2c_proto_type(sfp->mdio_protocol));

-	int ret;
-
 	if (sfp->mdio_protocol == MDIO_I2C_NONE)
 		return 0;

-	ret = sfp_i2c_mdiobus_create(sfp);
-	if (ret == -ENODEV) {
-		/* Probe confirmed no bridge present; skip PHY discovery. */
-		sfp->mdio_protocol = MDIO_I2C_NONE;
-		return 0;
-	}
-	return ret;
+	return sfp_i2c_mdiobus_create(sfp);
 }

 /* Probe a SFP for a PHY device if the module supports copper - the PHY
@@ -2058,8 +2049,23 @@ static int sfp_sm_probe_for_phy(struct sfp *sfp)

 	case MDIO_I2C_ROLLBALL:
-		err = sfp_sm_probe_phy(sfp, SFP_PHY_ADDR_ROLLBALL, true);
+		/* Probe here, after module initialization delays, so that
+		 * genuine RollBall bridges have had time to start up.
+		 * Modules without a bridge (e.g. RTL8261BE) return -ENODEV. */
+		err = mdio_i2c_probe_rollball(sfp->i2c);
+		if (err == -ENODEV) {
+			sfp_i2c_mdiobus_destroy(sfp);
+			sfp->mdio_protocol = MDIO_I2C_NONE;
+			break;
+		}
+		if (!err)
+			err = sfp_sm_probe_phy(sfp, SFP_PHY_ADDR_ROLLBALL, true);
 		break;
 	}
diff --git a/include/linux/mdio/mdio-i2c.h b/include/linux/mdio/mdio-i2c.h
--- a/include/linux/mdio/mdio-i2c.h
+++ b/include/linux/mdio/mdio-i2c.h
@@ -33,5 +33,6 @@

 struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c,
 			       enum mdio_i2c_proto protocol);
+int mdio_i2c_probe_rollball(struct i2c_adapter *i2c);

 #endif
--
2.51.0