Skip to content

Commit 8fe1258

Browse files
woziwrtkuba-moo
authored andcommitted
net: phy: sfp: probe for RollBall I2C-to-MDIO bridge in mdio-i2c
The "OEM"/"SFP-10G-T" quirk entry in sfp_fixup_rollball_cc() unconditionally forces MDIO_I2C_ROLLBALL for all modules matching that vendor/part-number combination. This works for modules that genuinely implement a RollBall I2C-to-MDIO bridge, but silently breaks modules that share the same EEPROM strings without having such a bridge. The Realtek RTL8261BE-CG is one such module: a pure copper 10G SFP+ media converter with no I2C-to-MDIO bridge. Its EEPROM reports vendor="OEM", part="SFP-10G-T-I", and -- critically -- Vendor OUI 00:00:00, making OUI-based differentiation impossible. With MDIO_I2C_ROLLBALL forced, the module silently ACKs the unlock password write, the MDIO bus is created, but no PHY responds; the SFP state machine cycles through the RollBall PHY-probe retry window before reporting no PHY. Move the probe into i2c_mii_init_rollball() in mdio-i2c.c, where the RollBall protocol constants are already defined. After sending the unlock password, issue a CMD_READ and poll for CMD_DONE up to 200 ms (10 x 20 ms, matching the existing rollball poll tolerance). A genuine RollBall bridge asserts CMD_DONE within that window; modules without a bridge never do, so i2c_mii_init_rollball() returns -ENODEV. mdio_i2c_alloc() propagates -ENODEV to the caller to signal that no bridge is present and PHY probing should be skipped. sfp_sm_add_mdio_bus() catches -ENODEV and transitions sfp->mdio_protocol to MDIO_I2C_NONE so the rest of the state machine skips PHY probing for this module. Any I2C-level error (NACK, timeout) during the probe is also treated as -ENODEV: if the module does not respond at I2C address 0x51 at all, there is certainly no RollBall bridge there, and SFP initialization should not abort. The probe writes are safe with respect to SFP EEPROM integrity: only modules explicitly listed in the quirk table enter this path, and the RollBall password unlock write to 0x51 was already issued by i2c_mii_init_rollball() before the probe for all such modules. Any module without a device at 0x51 NACKs the transfer and is treated as -ENODEV. Add "OEM"/"SFP-10G-T-I" to the quirk table so RTL8261BE modules enter the probe path; genuine RollBall modules continue to work as before. Signed-off-by: Petr Wozniak <petr.wozniak@gmail.com> Reviewed-by: Maxime Chevallier <maxime.chevallier@bootlin.com> Link: https://patch.msgid.link/20260527053909.2118-1-petr.wozniak@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
1 parent 925f3ec commit 8fe1258

2 files changed

Lines changed: 63 additions & 10 deletions

File tree

drivers/net/mdio/mdio-i2c.c

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,50 @@ static int i2c_mii_write_rollball(struct mii_bus *bus, int phy_id, int devad,
419419
return 0;
420420
}
421421

422+
static int i2c_mii_probe_rollball(struct i2c_adapter *i2c)
423+
{
424+
u8 data_buf[] = { ROLLBALL_DATA_ADDR, 0x01, 0x00, 0x00 };
425+
u8 cmd_buf[] = { ROLLBALL_CMD_ADDR, ROLLBALL_CMD_READ };
426+
u8 cmd_addr = ROLLBALL_CMD_ADDR;
427+
struct i2c_msg msgs[2];
428+
u8 result;
429+
int ret;
430+
int i;
431+
432+
msgs[0].addr = ROLLBALL_PHY_I2C_ADDR;
433+
msgs[0].flags = 0;
434+
msgs[0].len = sizeof(data_buf);
435+
msgs[0].buf = data_buf;
436+
msgs[1].addr = ROLLBALL_PHY_I2C_ADDR;
437+
msgs[1].flags = 0;
438+
msgs[1].len = sizeof(cmd_buf);
439+
msgs[1].buf = cmd_buf;
440+
441+
ret = i2c_transfer_rollball(i2c, msgs, ARRAY_SIZE(msgs));
442+
if (ret < 0)
443+
return -ENODEV;
444+
445+
msgs[0].addr = ROLLBALL_PHY_I2C_ADDR;
446+
msgs[0].flags = 0;
447+
msgs[0].len = 1;
448+
msgs[0].buf = &cmd_addr;
449+
msgs[1].addr = ROLLBALL_PHY_I2C_ADDR;
450+
msgs[1].flags = I2C_M_RD;
451+
msgs[1].len = 1;
452+
msgs[1].buf = &result;
453+
454+
for (i = 0; i < 10; i++) {
455+
msleep(20);
456+
ret = i2c_transfer_rollball(i2c, msgs, ARRAY_SIZE(msgs));
457+
if (ret < 0)
458+
return -ENODEV;
459+
if (result == ROLLBALL_CMD_DONE)
460+
return 0;
461+
}
462+
463+
return -ENODEV;
464+
}
465+
422466
static int i2c_mii_init_rollball(struct i2c_adapter *i2c)
423467
{
424468
struct i2c_msg msg;
@@ -438,11 +482,11 @@ static int i2c_mii_init_rollball(struct i2c_adapter *i2c)
438482

439483
ret = i2c_transfer(i2c, &msg, 1);
440484
if (ret < 0)
441-
return ret;
442-
else if (ret != 1)
485+
return -ENODEV;
486+
if (ret != 1)
443487
return -EIO;
444-
else
445-
return 0;
488+
489+
return i2c_mii_probe_rollball(i2c);
446490
}
447491

448492
static bool mdio_i2c_check_functionality(struct i2c_adapter *i2c,
@@ -487,9 +531,10 @@ struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c,
487531
case MDIO_I2C_ROLLBALL:
488532
ret = i2c_mii_init_rollball(i2c);
489533
if (ret < 0) {
490-
dev_err(parent,
491-
"Cannot initialize RollBall MDIO I2C protocol: %d\n",
492-
ret);
534+
if (ret != -ENODEV)
535+
dev_err(parent,
536+
"Cannot initialize RollBall MDIO I2C protocol: %d\n",
537+
ret);
493538
mdiobus_free(mii);
494539
return ERR_PTR(ret);
495540
}

drivers/net/phy/sfp.c

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,7 @@ static const struct sfp_quirk sfp_quirks[] = {
579579
// OEM SFP-GE-T is a 1000Base-T module with broken TX_FAULT indicator
580580
SFP_QUIRK_F("OEM", "SFP-GE-T", sfp_fixup_ignore_tx_fault),
581581

582+
SFP_QUIRK_F("OEM", "SFP-10G-T-I", sfp_fixup_rollball),
582583
SFP_QUIRK_F("OEM", "SFP-10G-T", sfp_fixup_rollball_cc),
583584
SFP_QUIRK_S("OEM", "SFP-2.5G-T", sfp_quirk_oem_2_5g),
584585
SFP_QUIRK_S("OEM", "SFP-2.5G-BX10-D", sfp_quirk_2500basex),
@@ -2024,10 +2025,17 @@ static void sfp_sm_fault(struct sfp *sfp, unsigned int next_state, bool warn)
20242025

20252026
static int sfp_sm_add_mdio_bus(struct sfp *sfp)
20262027
{
2027-
if (sfp->mdio_protocol != MDIO_I2C_NONE)
2028-
return sfp_i2c_mdiobus_create(sfp);
2028+
int ret;
20292029

2030-
return 0;
2030+
if (sfp->mdio_protocol == MDIO_I2C_NONE)
2031+
return 0;
2032+
2033+
ret = sfp_i2c_mdiobus_create(sfp);
2034+
if (ret == -ENODEV) {
2035+
sfp->mdio_protocol = MDIO_I2C_NONE;
2036+
return 0;
2037+
}
2038+
return ret;
20312039
}
20322040

20332041
/* Probe a SFP for a PHY device if the module supports copper - the PHY

0 commit comments

Comments
 (0)